@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 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 el = page.locator(sel).first();
17045
- if (await el.isVisible({ timeout: 2000 })) {
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.39",
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 el = page.locator(sel).first();
15604
- if (await el.isVisible({ timeout: 2000 })) {
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,5 +1,6 @@
1
1
  import type { Page } from "playwright";
2
2
  import type { Persona } from "../types/index.js";
3
+ export declare function isSessionCookie(cookieName: string): boolean;
3
4
  export interface LoginResult {
4
5
  success: boolean;
5
6
  method: "cookies" | "login" | "none";
@@ -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;AAoND;;;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"}
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.39",
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 el = page.locator(sel).first();
20153
- if (await el.isVisible({ timeout: 2000 })) {
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();
@@ -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.39",
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 el = page.locator(sel).first();
48648
- if (await el.isVisible({ timeout: 2000 })) {
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/testers",
3
- "version": "0.0.39",
3
+ "version": "0.0.40",
4
4
  "description": "AI-powered QA testing CLI — spawns cheap AI agents to test web apps with headless browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",