@hasna/testers 0.0.39 → 0.0.40
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.
- package/dist/cli/index.js +38 -6
- package/dist/index.js +30 -2
- package/dist/lib/persona-auth.d.ts +1 -0
- package/dist/lib/persona-auth.d.ts.map +1 -1
- package/dist/mcp/index.js +31 -3
- package/dist/server/index.js +31 -3
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -16937,10 +16937,16 @@ function isCredentialReference(value) {
|
|
|
16937
16937
|
var init_secrets_resolver = () => {};
|
|
16938
16938
|
|
|
16939
16939
|
// src/lib/persona-auth.ts
|
|
16940
|
+
function isSessionCookie(cookieName) {
|
|
16941
|
+
return !/(?:csrf|xsrf)/i.test(cookieName);
|
|
16942
|
+
}
|
|
16940
16943
|
function areCookiesFresh(persona) {
|
|
16941
16944
|
if (!persona.auth?.cookies?.length)
|
|
16942
16945
|
return false;
|
|
16943
16946
|
const cookies = persona.auth.cookies;
|
|
16947
|
+
if (!cookies.some((cookie) => ("name" in cookie) && isSessionCookie(String(cookie.name)))) {
|
|
16948
|
+
return false;
|
|
16949
|
+
}
|
|
16944
16950
|
const now2 = Date.now() / 1000;
|
|
16945
16951
|
const hasFutureExpiry = cookies.some((c) => c.expires && c.expires > now2 + 60);
|
|
16946
16952
|
if (hasFutureExpiry)
|
|
@@ -16982,6 +16988,7 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
16982
16988
|
const loginUrl = auth.loginPath.startsWith("http") ? auth.loginPath : `${baseUrl.replace(/\/$/, "")}${auth.loginPath}`;
|
|
16983
16989
|
try {
|
|
16984
16990
|
await page.goto(loginUrl, { timeout: 30000, waitUntil: "domcontentloaded" });
|
|
16991
|
+
await page.waitForLoadState("networkidle", { timeout: 1e4 }).catch(() => {});
|
|
16985
16992
|
} catch (err) {
|
|
16986
16993
|
return { success: false, method: "login", error: `Navigation to login page failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
16987
16994
|
}
|
|
@@ -17038,11 +17045,25 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
17038
17045
|
if (!passwordFilled) {
|
|
17039
17046
|
return { success: false, method: "login", error: "Could not find password field on login page" };
|
|
17040
17047
|
}
|
|
17048
|
+
await page.waitForFunction(() => {
|
|
17049
|
+
const submits = Array.from(document.querySelectorAll('button[type="submit"], input[type="submit"]'));
|
|
17050
|
+
return submits.length === 0 || submits.some((submit) => {
|
|
17051
|
+
const rect = submit.getBoundingClientRect();
|
|
17052
|
+
const visible = rect.width > 0 || rect.height > 0 || submit.getClientRects().length > 0;
|
|
17053
|
+
return visible && !submit.disabled;
|
|
17054
|
+
});
|
|
17055
|
+
}, null, { timeout: 5000 }).catch(() => {});
|
|
17041
17056
|
let submitted = false;
|
|
17042
17057
|
for (const sel of submitSelectors) {
|
|
17043
17058
|
try {
|
|
17044
|
-
const
|
|
17045
|
-
|
|
17059
|
+
const matches = page.locator(sel);
|
|
17060
|
+
const count = await matches.count().catch(() => 0);
|
|
17061
|
+
for (let i = 0;i < Math.min(count, 10); i++) {
|
|
17062
|
+
const el = matches.nth(i);
|
|
17063
|
+
if (!await el.isVisible({ timeout: 500 }).catch(() => false))
|
|
17064
|
+
continue;
|
|
17065
|
+
if (!await el.isEnabled({ timeout: 2000 }).catch(() => false))
|
|
17066
|
+
continue;
|
|
17046
17067
|
await Promise.all([
|
|
17047
17068
|
page.waitForNavigation({ timeout: 15000, waitUntil: "domcontentloaded" }).catch(() => {}),
|
|
17048
17069
|
el.click({ timeout: 5000 })
|
|
@@ -17050,6 +17071,8 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
17050
17071
|
submitted = true;
|
|
17051
17072
|
break;
|
|
17052
17073
|
}
|
|
17074
|
+
if (submitted)
|
|
17075
|
+
break;
|
|
17053
17076
|
} catch {}
|
|
17054
17077
|
}
|
|
17055
17078
|
if (!submitted) {
|
|
@@ -17066,6 +17089,11 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
17066
17089
|
const currentUrl = page.url();
|
|
17067
17090
|
const isStillOnLogin = currentUrl.includes(auth.loginPath) || currentUrl.includes("/login") || currentUrl.includes("/signin") || currentUrl.includes("/auth");
|
|
17068
17091
|
if (isStillOnLogin) {
|
|
17092
|
+
const cookies = await page.context().cookies().catch(() => []);
|
|
17093
|
+
const authCookies = cookies.filter((cookie) => isSessionCookie(cookie.name));
|
|
17094
|
+
if (authCookies.length > 0) {
|
|
17095
|
+
return { success: true, method: "login" };
|
|
17096
|
+
}
|
|
17069
17097
|
let errorText = "";
|
|
17070
17098
|
try {
|
|
17071
17099
|
const errorEl = page.locator('[role="alert"], .error, .alert-error, [data-testid*="error"]').first();
|
|
@@ -93997,7 +94025,7 @@ import chalk6 from "chalk";
|
|
|
93997
94025
|
// package.json
|
|
93998
94026
|
var package_default = {
|
|
93999
94027
|
name: "@hasna/testers",
|
|
94000
|
-
version: "0.0.
|
|
94028
|
+
version: "0.0.40",
|
|
94001
94029
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
94002
94030
|
type: "module",
|
|
94003
94031
|
main: "dist/index.js",
|
|
@@ -97629,6 +97657,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
|
|
|
97629
97657
|
process.exit(2);
|
|
97630
97658
|
}, overallTimeoutMs).unref();
|
|
97631
97659
|
}
|
|
97660
|
+
const personaIdList = opts.persona ? opts.persona.split(",").map((s2) => s2.trim()).filter(Boolean) : undefined;
|
|
97632
97661
|
if (!opts.dryRun && !opts.background) {
|
|
97633
97662
|
const budgetResult = checkBudget(0);
|
|
97634
97663
|
if (budgetResult.warning) {
|
|
@@ -97712,7 +97741,9 @@ program2.command("run [url] [description]").alias("test").description("Run test
|
|
|
97712
97741
|
timeout: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
|
|
97713
97742
|
maxTurns: opts.maxTurns ? parseInt(opts.maxTurns, 10) : undefined,
|
|
97714
97743
|
projectId,
|
|
97715
|
-
engine: opts.browser
|
|
97744
|
+
engine: opts.browser,
|
|
97745
|
+
personaId: personaIdList?.[0],
|
|
97746
|
+
personaIds: personaIdList && personaIdList.length > 1 ? personaIdList : undefined
|
|
97716
97747
|
});
|
|
97717
97748
|
log(chalk6.green(`Run started in background: ${chalk6.bold(runId.slice(0, 8))}`));
|
|
97718
97749
|
log(chalk6.dim(` Scenarios: ${scenarioCount}`));
|
|
@@ -97836,7 +97867,9 @@ program2.command("run [url] [description]").alias("test").description("Run test
|
|
|
97836
97867
|
samples: parseInt(opts.samples ?? "1", 10),
|
|
97837
97868
|
flakinessThreshold: parseFloat(opts.flakinessThreshold ?? "0.95"),
|
|
97838
97869
|
a11y: opts.a11y ? typeof opts.a11y === "string" ? { level: opts.a11y } : true : undefined,
|
|
97839
|
-
selfHeal: opts.selfHeal || undefined
|
|
97870
|
+
selfHeal: opts.selfHeal || undefined,
|
|
97871
|
+
personaId: personaIdList?.[0],
|
|
97872
|
+
personaIds: personaIdList && personaIdList.length > 1 ? personaIdList : undefined
|
|
97840
97873
|
});
|
|
97841
97874
|
if (opts.json || opts.output) {
|
|
97842
97875
|
const jsonOutput = formatJSON(run3, results2);
|
|
@@ -97924,7 +97957,6 @@ program2.command("run [url] [description]").alias("test").description("Run test
|
|
|
97924
97957
|
log(chalk6.yellow(" --diff: git diff failed. Running all scenarios."));
|
|
97925
97958
|
}
|
|
97926
97959
|
}
|
|
97927
|
-
const personaIdList = opts.persona ? opts.persona.split(",").map((s2) => s2.trim()).filter(Boolean) : undefined;
|
|
97928
97960
|
const { run: run2, results } = await runByFilter({
|
|
97929
97961
|
url: url2,
|
|
97930
97962
|
tags: opts.tag.length > 0 ? opts.tag : undefined,
|
package/dist/index.js
CHANGED
|
@@ -15496,10 +15496,16 @@ function isCredentialReference(value) {
|
|
|
15496
15496
|
|
|
15497
15497
|
// src/lib/persona-auth.ts
|
|
15498
15498
|
var COOKIE_MAX_AGE_MS = 60 * 60 * 1000;
|
|
15499
|
+
function isSessionCookie(cookieName) {
|
|
15500
|
+
return !/(?:csrf|xsrf)/i.test(cookieName);
|
|
15501
|
+
}
|
|
15499
15502
|
function areCookiesFresh(persona) {
|
|
15500
15503
|
if (!persona.auth?.cookies?.length)
|
|
15501
15504
|
return false;
|
|
15502
15505
|
const cookies = persona.auth.cookies;
|
|
15506
|
+
if (!cookies.some((cookie) => ("name" in cookie) && isSessionCookie(String(cookie.name)))) {
|
|
15507
|
+
return false;
|
|
15508
|
+
}
|
|
15503
15509
|
const now2 = Date.now() / 1000;
|
|
15504
15510
|
const hasFutureExpiry = cookies.some((c) => c.expires && c.expires > now2 + 60);
|
|
15505
15511
|
if (hasFutureExpiry)
|
|
@@ -15541,6 +15547,7 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
15541
15547
|
const loginUrl = auth.loginPath.startsWith("http") ? auth.loginPath : `${baseUrl.replace(/\/$/, "")}${auth.loginPath}`;
|
|
15542
15548
|
try {
|
|
15543
15549
|
await page.goto(loginUrl, { timeout: 30000, waitUntil: "domcontentloaded" });
|
|
15550
|
+
await page.waitForLoadState("networkidle", { timeout: 1e4 }).catch(() => {});
|
|
15544
15551
|
} catch (err) {
|
|
15545
15552
|
return { success: false, method: "login", error: `Navigation to login page failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
15546
15553
|
}
|
|
@@ -15597,11 +15604,25 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
15597
15604
|
if (!passwordFilled) {
|
|
15598
15605
|
return { success: false, method: "login", error: "Could not find password field on login page" };
|
|
15599
15606
|
}
|
|
15607
|
+
await page.waitForFunction(() => {
|
|
15608
|
+
const submits = Array.from(document.querySelectorAll('button[type="submit"], input[type="submit"]'));
|
|
15609
|
+
return submits.length === 0 || submits.some((submit) => {
|
|
15610
|
+
const rect = submit.getBoundingClientRect();
|
|
15611
|
+
const visible = rect.width > 0 || rect.height > 0 || submit.getClientRects().length > 0;
|
|
15612
|
+
return visible && !submit.disabled;
|
|
15613
|
+
});
|
|
15614
|
+
}, null, { timeout: 5000 }).catch(() => {});
|
|
15600
15615
|
let submitted = false;
|
|
15601
15616
|
for (const sel of submitSelectors) {
|
|
15602
15617
|
try {
|
|
15603
|
-
const
|
|
15604
|
-
|
|
15618
|
+
const matches = page.locator(sel);
|
|
15619
|
+
const count = await matches.count().catch(() => 0);
|
|
15620
|
+
for (let i = 0;i < Math.min(count, 10); i++) {
|
|
15621
|
+
const el = matches.nth(i);
|
|
15622
|
+
if (!await el.isVisible({ timeout: 500 }).catch(() => false))
|
|
15623
|
+
continue;
|
|
15624
|
+
if (!await el.isEnabled({ timeout: 2000 }).catch(() => false))
|
|
15625
|
+
continue;
|
|
15605
15626
|
await Promise.all([
|
|
15606
15627
|
page.waitForNavigation({ timeout: 15000, waitUntil: "domcontentloaded" }).catch(() => {}),
|
|
15607
15628
|
el.click({ timeout: 5000 })
|
|
@@ -15609,6 +15630,8 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
15609
15630
|
submitted = true;
|
|
15610
15631
|
break;
|
|
15611
15632
|
}
|
|
15633
|
+
if (submitted)
|
|
15634
|
+
break;
|
|
15612
15635
|
} catch {}
|
|
15613
15636
|
}
|
|
15614
15637
|
if (!submitted) {
|
|
@@ -15625,6 +15648,11 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
15625
15648
|
const currentUrl = page.url();
|
|
15626
15649
|
const isStillOnLogin = currentUrl.includes(auth.loginPath) || currentUrl.includes("/login") || currentUrl.includes("/signin") || currentUrl.includes("/auth");
|
|
15627
15650
|
if (isStillOnLogin) {
|
|
15651
|
+
const cookies = await page.context().cookies().catch(() => []);
|
|
15652
|
+
const authCookies = cookies.filter((cookie) => isSessionCookie(cookie.name));
|
|
15653
|
+
if (authCookies.length > 0) {
|
|
15654
|
+
return { success: true, method: "login" };
|
|
15655
|
+
}
|
|
15628
15656
|
let errorText = "";
|
|
15629
15657
|
try {
|
|
15630
15658
|
const errorEl = page.locator('[role="alert"], .error, .alert-error, [data-testid*="error"]').first();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persona-auth.d.ts","sourceRoot":"","sources":["../../src/lib/persona-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAMjD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;
|
|
1
|
+
{"version":3,"file":"persona-auth.d.ts","sourceRoot":"","sources":["../../src/lib/persona-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAMjD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+OD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACvG,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CAgCtB;AAED;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CAsBtB"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "@hasna/testers",
|
|
55
|
-
version: "0.0.
|
|
55
|
+
version: "0.0.40",
|
|
56
56
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
57
57
|
type: "module",
|
|
58
58
|
main: "dist/index.js",
|
|
@@ -20045,10 +20045,16 @@ function isCredentialReference(value) {
|
|
|
20045
20045
|
var init_secrets_resolver = () => {};
|
|
20046
20046
|
|
|
20047
20047
|
// src/lib/persona-auth.ts
|
|
20048
|
+
function isSessionCookie(cookieName) {
|
|
20049
|
+
return !/(?:csrf|xsrf)/i.test(cookieName);
|
|
20050
|
+
}
|
|
20048
20051
|
function areCookiesFresh(persona) {
|
|
20049
20052
|
if (!persona.auth?.cookies?.length)
|
|
20050
20053
|
return false;
|
|
20051
20054
|
const cookies = persona.auth.cookies;
|
|
20055
|
+
if (!cookies.some((cookie) => ("name" in cookie) && isSessionCookie(String(cookie.name)))) {
|
|
20056
|
+
return false;
|
|
20057
|
+
}
|
|
20052
20058
|
const now2 = Date.now() / 1000;
|
|
20053
20059
|
const hasFutureExpiry = cookies.some((c) => c.expires && c.expires > now2 + 60);
|
|
20054
20060
|
if (hasFutureExpiry)
|
|
@@ -20090,6 +20096,7 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
20090
20096
|
const loginUrl = auth.loginPath.startsWith("http") ? auth.loginPath : `${baseUrl.replace(/\/$/, "")}${auth.loginPath}`;
|
|
20091
20097
|
try {
|
|
20092
20098
|
await page.goto(loginUrl, { timeout: 30000, waitUntil: "domcontentloaded" });
|
|
20099
|
+
await page.waitForLoadState("networkidle", { timeout: 1e4 }).catch(() => {});
|
|
20093
20100
|
} catch (err) {
|
|
20094
20101
|
return { success: false, method: "login", error: `Navigation to login page failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
20095
20102
|
}
|
|
@@ -20146,11 +20153,25 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
20146
20153
|
if (!passwordFilled) {
|
|
20147
20154
|
return { success: false, method: "login", error: "Could not find password field on login page" };
|
|
20148
20155
|
}
|
|
20156
|
+
await page.waitForFunction(() => {
|
|
20157
|
+
const submits = Array.from(document.querySelectorAll('button[type="submit"], input[type="submit"]'));
|
|
20158
|
+
return submits.length === 0 || submits.some((submit) => {
|
|
20159
|
+
const rect = submit.getBoundingClientRect();
|
|
20160
|
+
const visible = rect.width > 0 || rect.height > 0 || submit.getClientRects().length > 0;
|
|
20161
|
+
return visible && !submit.disabled;
|
|
20162
|
+
});
|
|
20163
|
+
}, null, { timeout: 5000 }).catch(() => {});
|
|
20149
20164
|
let submitted = false;
|
|
20150
20165
|
for (const sel of submitSelectors) {
|
|
20151
20166
|
try {
|
|
20152
|
-
const
|
|
20153
|
-
|
|
20167
|
+
const matches = page.locator(sel);
|
|
20168
|
+
const count = await matches.count().catch(() => 0);
|
|
20169
|
+
for (let i = 0;i < Math.min(count, 10); i++) {
|
|
20170
|
+
const el = matches.nth(i);
|
|
20171
|
+
if (!await el.isVisible({ timeout: 500 }).catch(() => false))
|
|
20172
|
+
continue;
|
|
20173
|
+
if (!await el.isEnabled({ timeout: 2000 }).catch(() => false))
|
|
20174
|
+
continue;
|
|
20154
20175
|
await Promise.all([
|
|
20155
20176
|
page.waitForNavigation({ timeout: 15000, waitUntil: "domcontentloaded" }).catch(() => {}),
|
|
20156
20177
|
el.click({ timeout: 5000 })
|
|
@@ -20158,6 +20179,8 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
20158
20179
|
submitted = true;
|
|
20159
20180
|
break;
|
|
20160
20181
|
}
|
|
20182
|
+
if (submitted)
|
|
20183
|
+
break;
|
|
20161
20184
|
} catch {}
|
|
20162
20185
|
}
|
|
20163
20186
|
if (!submitted) {
|
|
@@ -20174,6 +20197,11 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
20174
20197
|
const currentUrl = page.url();
|
|
20175
20198
|
const isStillOnLogin = currentUrl.includes(auth.loginPath) || currentUrl.includes("/login") || currentUrl.includes("/signin") || currentUrl.includes("/auth");
|
|
20176
20199
|
if (isStillOnLogin) {
|
|
20200
|
+
const cookies = await page.context().cookies().catch(() => []);
|
|
20201
|
+
const authCookies = cookies.filter((cookie) => isSessionCookie(cookie.name));
|
|
20202
|
+
if (authCookies.length > 0) {
|
|
20203
|
+
return { success: true, method: "login" };
|
|
20204
|
+
}
|
|
20177
20205
|
let errorText = "";
|
|
20178
20206
|
try {
|
|
20179
20207
|
const errorEl = page.locator('[role="alert"], .error, .alert-error, [data-testid*="error"]').first();
|
package/dist/server/index.js
CHANGED
|
@@ -46910,7 +46910,7 @@ import { join as join14 } from "path";
|
|
|
46910
46910
|
// package.json
|
|
46911
46911
|
var package_default = {
|
|
46912
46912
|
name: "@hasna/testers",
|
|
46913
|
-
version: "0.0.
|
|
46913
|
+
version: "0.0.40",
|
|
46914
46914
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
46915
46915
|
type: "module",
|
|
46916
46916
|
main: "dist/index.js",
|
|
@@ -48540,10 +48540,16 @@ function resolveCredential(value) {
|
|
|
48540
48540
|
|
|
48541
48541
|
// src/lib/persona-auth.ts
|
|
48542
48542
|
var COOKIE_MAX_AGE_MS = 60 * 60 * 1000;
|
|
48543
|
+
function isSessionCookie(cookieName) {
|
|
48544
|
+
return !/(?:csrf|xsrf)/i.test(cookieName);
|
|
48545
|
+
}
|
|
48543
48546
|
function areCookiesFresh(persona) {
|
|
48544
48547
|
if (!persona.auth?.cookies?.length)
|
|
48545
48548
|
return false;
|
|
48546
48549
|
const cookies = persona.auth.cookies;
|
|
48550
|
+
if (!cookies.some((cookie) => ("name" in cookie) && isSessionCookie(String(cookie.name)))) {
|
|
48551
|
+
return false;
|
|
48552
|
+
}
|
|
48547
48553
|
const now2 = Date.now() / 1000;
|
|
48548
48554
|
const hasFutureExpiry = cookies.some((c) => c.expires && c.expires > now2 + 60);
|
|
48549
48555
|
if (hasFutureExpiry)
|
|
@@ -48585,6 +48591,7 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
48585
48591
|
const loginUrl = auth.loginPath.startsWith("http") ? auth.loginPath : `${baseUrl.replace(/\/$/, "")}${auth.loginPath}`;
|
|
48586
48592
|
try {
|
|
48587
48593
|
await page.goto(loginUrl, { timeout: 30000, waitUntil: "domcontentloaded" });
|
|
48594
|
+
await page.waitForLoadState("networkidle", { timeout: 1e4 }).catch(() => {});
|
|
48588
48595
|
} catch (err) {
|
|
48589
48596
|
return { success: false, method: "login", error: `Navigation to login page failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
48590
48597
|
}
|
|
@@ -48641,11 +48648,25 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
48641
48648
|
if (!passwordFilled) {
|
|
48642
48649
|
return { success: false, method: "login", error: "Could not find password field on login page" };
|
|
48643
48650
|
}
|
|
48651
|
+
await page.waitForFunction(() => {
|
|
48652
|
+
const submits = Array.from(document.querySelectorAll('button[type="submit"], input[type="submit"]'));
|
|
48653
|
+
return submits.length === 0 || submits.some((submit) => {
|
|
48654
|
+
const rect = submit.getBoundingClientRect();
|
|
48655
|
+
const visible = rect.width > 0 || rect.height > 0 || submit.getClientRects().length > 0;
|
|
48656
|
+
return visible && !submit.disabled;
|
|
48657
|
+
});
|
|
48658
|
+
}, null, { timeout: 5000 }).catch(() => {});
|
|
48644
48659
|
let submitted = false;
|
|
48645
48660
|
for (const sel of submitSelectors) {
|
|
48646
48661
|
try {
|
|
48647
|
-
const
|
|
48648
|
-
|
|
48662
|
+
const matches = page.locator(sel);
|
|
48663
|
+
const count = await matches.count().catch(() => 0);
|
|
48664
|
+
for (let i = 0;i < Math.min(count, 10); i++) {
|
|
48665
|
+
const el = matches.nth(i);
|
|
48666
|
+
if (!await el.isVisible({ timeout: 500 }).catch(() => false))
|
|
48667
|
+
continue;
|
|
48668
|
+
if (!await el.isEnabled({ timeout: 2000 }).catch(() => false))
|
|
48669
|
+
continue;
|
|
48649
48670
|
await Promise.all([
|
|
48650
48671
|
page.waitForNavigation({ timeout: 15000, waitUntil: "domcontentloaded" }).catch(() => {}),
|
|
48651
48672
|
el.click({ timeout: 5000 })
|
|
@@ -48653,6 +48674,8 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
48653
48674
|
submitted = true;
|
|
48654
48675
|
break;
|
|
48655
48676
|
}
|
|
48677
|
+
if (submitted)
|
|
48678
|
+
break;
|
|
48656
48679
|
} catch {}
|
|
48657
48680
|
}
|
|
48658
48681
|
if (!submitted) {
|
|
@@ -48669,6 +48692,11 @@ async function performLogin(page, persona, baseUrl) {
|
|
|
48669
48692
|
const currentUrl = page.url();
|
|
48670
48693
|
const isStillOnLogin = currentUrl.includes(auth.loginPath) || currentUrl.includes("/login") || currentUrl.includes("/signin") || currentUrl.includes("/auth");
|
|
48671
48694
|
if (isStillOnLogin) {
|
|
48695
|
+
const cookies = await page.context().cookies().catch(() => []);
|
|
48696
|
+
const authCookies = cookies.filter((cookie) => isSessionCookie(cookie.name));
|
|
48697
|
+
if (authCookies.length > 0) {
|
|
48698
|
+
return { success: true, method: "login" };
|
|
48699
|
+
}
|
|
48672
48700
|
let errorText = "";
|
|
48673
48701
|
try {
|
|
48674
48702
|
const errorEl = page.locator('[role="alert"], .error, .alert-error, [data-testid*="error"]').first();
|