testronaut 1.0.25 → 1.0.26

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testronaut",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "main": "./runner/testronaut.js",
5
5
  "bin": {
6
6
  "testronaut": "./bin/cli.js"
@@ -504,11 +504,24 @@ export class ChromeBrowser {
504
504
  throw new Error('Could not input text with any strategy');
505
505
  }
506
506
 
507
+ _chromiumLaunchArgsForCI() {
508
+ const inCI = !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.TESTRONAUT_CI_CHROMIUM);
509
+ return inCI ? ['--no-sandbox', '--disable-setuid-sandbox'] : [];
510
+ }
511
+
507
512
 
508
513
  async start() {
509
514
  try {
510
515
  const pw = await loadPlaywright();
511
- this.browser = await pw.chromium.launch({ headless: true });
516
+ const launchArgs = [
517
+ '--disable-dev-shm-usage',
518
+ '--disable-gpu',
519
+ ...this._chromiumLaunchArgsForCI(),
520
+ ];
521
+ this.browser = await pw.chromium.launch({
522
+ headless: true,
523
+ args: launchArgs,
524
+ });
512
525
  } catch (err) {
513
526
  const msg = String(err?.message || err);
514
527
  const needsInstall =
@@ -525,7 +538,16 @@ export class ChromeBrowser {
525
538
 
526
539
  // Retry
527
540
  const pw = await loadPlaywright();
528
- return await pw.chromium.launch({ headless: true });
541
+ const launchArgs = [
542
+ '--disable-dev-shm-usage',
543
+ '--disable-gpu',
544
+ ...this._chromiumLaunchArgsForCI(),
545
+ ];
546
+ this.browser = await pw.chromium.launch({
547
+ headless: true,
548
+ args: launchArgs,
549
+ });
550
+ // return await pw.chromium.launch({ headless: true });
529
551
  }
530
552
  this.context = await this.browser.newContext();
531
553
  const first = await this.context.newPage();
@@ -740,34 +762,97 @@ export class ChromeBrowser {
740
762
 
741
763
 
742
764
 
743
- async get_dom({ limit = 100000, exclude = true, focus = [] }, agentMemory = {}) {
744
- let html;
765
+ async get_dom(
766
+ { limit = 100000, exclude = true, focus = [], loadState = 'networkidle', timeout = 10000 } = {},
767
+ agentMemory = {}
768
+ ) {
769
+ // --- 1) Ensure we have a live page ---
770
+ const hasContext = !!this.context;
771
+
772
+ // Recover a page if missing/closed
773
+ const recoverPage = async () => {
774
+ if (hasContext) {
775
+ const pages = (this.context.pages?.() || []).filter(p => !p.isClosed?.());
776
+ if (pages.length) {
777
+ // Prefer the last page (often the active one / popup)
778
+ this.page = pages[pages.length - 1];
779
+ return;
780
+ }
781
+ // No pages at all in a live context -> make one
782
+ this.page = await this.context.newPage();
783
+ return;
784
+ }
785
+ // No context yet but a browser exists -> create context + page
786
+ if (this.browser?.newContext) {
787
+ this.context = await this.browser.newContext();
788
+ this.page = await this.context.newPage();
789
+ return;
790
+ }
791
+ throw new Error('No browser context/page available in get_dom().');
792
+ };
793
+
794
+ if (!this.page || this.page.isClosed?.()) {
795
+ await recoverPage();
796
+ }
797
+
798
+ // --- 2) Try to reach a usable load state (best-effort) ---
745
799
  try {
746
- html = await this.page.content();
747
- } catch (err) {
748
- console.warn('⚠️ get_dom retrying due to navigation:', err.message);
749
- // Wait for navigation to settle
750
- await this.page.waitForLoadState('networkidle', { timeout: 5000 }).catch(() => {});
751
- html = await this.page.content();
800
+ await this.page.waitForLoadState(loadState, { timeout });
801
+ } catch {
802
+ // Non-fatal: some apps never reach 'load', we’ll still try to read the DOM.
803
+ }
804
+
805
+ // If navigation changed active page (e.g., popup), reselect a live page
806
+ try {
807
+ const pages = this.context?.pages?.() || [];
808
+ if (pages.length && (this.page.isClosed?.() || !pages.includes(this.page))) {
809
+ this.page = pages[pages.length - 1];
810
+ }
811
+ } catch { /* ignore */ }
812
+
813
+ // --- 3) Get HTML content with a small retry on failure ---
814
+ let html = '';
815
+ for (let attempt = 0; attempt < 2; attempt++) {
816
+ try {
817
+ html = await this.page.content();
818
+ if (html && html.trim()) break;
819
+ } catch (err) {
820
+ // First failure: try waiting for network to settle, then retry
821
+ if (attempt === 0) {
822
+ try { await this.page.waitForLoadState('networkidle', { timeout: 5000 }); } catch {}
823
+ // Also re-check active page (popups can swap)
824
+ try {
825
+ const pages = this.context?.pages?.() || [];
826
+ if (pages.length) this.page = pages[pages.length - 1];
827
+ } catch {}
828
+ } else {
829
+ // Second failure: bubble up a clearer message
830
+ throw new Error(`get_dom() failed to read page content: ${err?.message || err}`);
831
+ }
832
+ }
752
833
  }
834
+
835
+ // Fallback to empty string if still nothing (keeps caller logic simple)
836
+ if (!html) html = '';
837
+
838
+ // --- 4) Optional DOM reduction/cleanup (your existing steps) ---
753
839
  let $ = cheerio.load(html);
754
-
755
- // Remove non-content tags
840
+
756
841
  if (exclude) {
757
842
  $ = await removeNonContentTags($);
758
- // console.log('🔧 Non-content tags removed from DOM.');
759
843
  $ = await reduceRepeatedDivs($);
760
- // console.log('🔧 Repeated divs reduced in DOM.');
844
+ // If you later re-enable:
761
845
  // $ = await removeObfuscatedClassNames($);
762
- // console.log('🔧 Obfuscated class names removed from DOM.')
763
846
  // $ = await removeAutoGeneratedClasses($);
764
- // console.log('🔧 Auto-generated classes removed from DOM.');
765
847
  }
766
- // console.log('🔧 DOM cleaned up:', $);
848
+
849
+ // (Optional: If you'd like to use `focus`, you could filter here by selectors/keywords.)
850
+
851
+ // --- 5) Return trimmed HTML ---
767
852
  const finalHtml = $.html();
768
- const trimmed = finalHtml.slice(0, limit);
769
- return trimmed;
853
+ return finalHtml.slice(0, limit);
770
854
  }
855
+
771
856
 
772
857
  async check_text({ text }) {
773
858
  const html = await this.page.content();