@sudobility/testomniac_runner_service 0.1.136 → 0.1.137

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.
Files changed (46) hide show
  1. package/dist/analyzer/page-analyzer/generators/content.d.ts +2 -1
  2. package/dist/analyzer/page-analyzer/generators/content.d.ts.map +1 -1
  3. package/dist/analyzer/page-analyzer/generators/content.js +32 -39
  4. package/dist/analyzer/page-analyzer/generators/content.js.map +1 -1
  5. package/dist/analyzer/page-analyzer/generators/dialogs.d.ts +2 -1
  6. package/dist/analyzer/page-analyzer/generators/dialogs.d.ts.map +1 -1
  7. package/dist/analyzer/page-analyzer/generators/dialogs.js +25 -37
  8. package/dist/analyzer/page-analyzer/generators/dialogs.js.map +1 -1
  9. package/dist/analyzer/page-analyzer/generators/e2e.d.ts +2 -1
  10. package/dist/analyzer/page-analyzer/generators/e2e.d.ts.map +1 -1
  11. package/dist/analyzer/page-analyzer/generators/e2e.js +30 -37
  12. package/dist/analyzer/page-analyzer/generators/e2e.js.map +1 -1
  13. package/dist/analyzer/page-analyzer/generators/forms.d.ts +2 -1
  14. package/dist/analyzer/page-analyzer/generators/forms.d.ts.map +1 -1
  15. package/dist/analyzer/page-analyzer/generators/forms.js +29 -45
  16. package/dist/analyzer/page-analyzer/generators/forms.js.map +1 -1
  17. package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.d.ts +2 -1
  18. package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.d.ts.map +1 -1
  19. package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.js +25 -37
  20. package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.js.map +1 -1
  21. package/dist/analyzer/page-analyzer/generators/render.d.ts +2 -1
  22. package/dist/analyzer/page-analyzer/generators/render.d.ts.map +1 -1
  23. package/dist/analyzer/page-analyzer/generators/render.js +26 -32
  24. package/dist/analyzer/page-analyzer/generators/render.js.map +1 -1
  25. package/dist/analyzer/page-analyzer/generators/scaffolds.d.ts +2 -1
  26. package/dist/analyzer/page-analyzer/generators/scaffolds.d.ts.map +1 -1
  27. package/dist/analyzer/page-analyzer/generators/scaffolds.js +27 -44
  28. package/dist/analyzer/page-analyzer/generators/scaffolds.js.map +1 -1
  29. package/dist/analyzer/page-analyzer/generators/semantic-journeys.d.ts +2 -1
  30. package/dist/analyzer/page-analyzer/generators/semantic-journeys.d.ts.map +1 -1
  31. package/dist/analyzer/page-analyzer/generators/semantic-journeys.js +25 -37
  32. package/dist/analyzer/page-analyzer/generators/semantic-journeys.js.map +1 -1
  33. package/dist/analyzer/page-analyzer/generators/variants.d.ts +2 -1
  34. package/dist/analyzer/page-analyzer/generators/variants.d.ts.map +1 -1
  35. package/dist/analyzer/page-analyzer/generators/variants.js +25 -37
  36. package/dist/analyzer/page-analyzer/generators/variants.js.map +1 -1
  37. package/dist/analyzer/page-analyzer/index.d.ts.map +1 -1
  38. package/dist/analyzer/page-analyzer/index.js +96 -82
  39. package/dist/analyzer/page-analyzer/index.js.map +1 -1
  40. package/dist/api/client.d.ts +4 -1
  41. package/dist/api/client.d.ts.map +1 -1
  42. package/dist/api/client.js +18 -0
  43. package/dist/api/client.js.map +1 -1
  44. package/dist/orchestrator/test-interaction-executor.js +18 -15
  45. package/dist/orchestrator/test-interaction-executor.js.map +1 -1
  46. package/package.json +3 -3
@@ -424,20 +424,43 @@ export class PageAnalyzer {
424
424
  currentTestInteractionId: contextForFullPass.currentTestInteractionId,
425
425
  currentPageStateId: contextForFullPass.currentPageStateId,
426
426
  });
427
- await generateRenderTestInteractions(this, contextForFullPass);
428
- await generateFormTestInteractions(this, contextForFullPass);
429
- // Login test generation: detect login forms in context and generate
430
- // login-specific tests if the page appears to be a login page
427
+ // Collect all standard generator outputs (they return data, no API calls)
428
+ const outputs = [];
429
+ outputs.push(await generateRenderTestInteractions(this, contextForFullPass));
430
+ outputs.push(await generateFormTestInteractions(this, contextForFullPass));
431
+ // Login test generation: calls API directly (dependency chains)
431
432
  if (contextForFullPass.loginDetection?.isLoginPage) {
432
433
  await generateLoginTestInteractions(this, contextForFullPass, contextForFullPass.loginDetection, contextForFullPass.loginConfig);
433
434
  }
434
- await generateSemanticJourneyTestInteractions(this, contextForFullPass);
435
- await generateE2ETestInteractions(this, contextForFullPass);
436
- await generateDialogLifecycleTestInteractions(this, contextForFullPass);
437
- await generateScaffoldTestInteractions(this, contextForFullPass);
438
- await generateContentTestInteractions(this, contextForFullPass);
439
- await generateKeyboardAndDisclosureTestInteractions(this, contextForFullPass);
440
- await generateVariantTestInteractions(this, contextForFullPass);
435
+ outputs.push(await generateSemanticJourneyTestInteractions(this, contextForFullPass));
436
+ outputs.push(await generateE2ETestInteractions(this, contextForFullPass));
437
+ outputs.push(await generateDialogLifecycleTestInteractions(this, contextForFullPass));
438
+ outputs.push(await generateScaffoldTestInteractions(this, contextForFullPass));
439
+ outputs.push(await generateContentTestInteractions(this, contextForFullPass));
440
+ outputs.push(await generateKeyboardAndDisclosureTestInteractions(this, contextForFullPass));
441
+ outputs.push(await generateVariantTestInteractions(this, contextForFullPass));
442
+ // Batch all collected generator outputs in one API call
443
+ const allCreates = outputs.flatMap(o => o.creates);
444
+ const allReconciles = outputs.flatMap(o => o.reconciles);
445
+ if (allCreates.length > 0 || allReconciles.length > 0) {
446
+ const batchResult = await contextForFullPass.api.generateAllSurfaceInteractions({
447
+ runnerId: contextForFullPass.runnerId,
448
+ testEnvironmentId: contextForFullPass.testEnvironmentId,
449
+ sizeClass: contextForFullPass.sizeClass,
450
+ testSurfaceBundleId: contextForFullPass.bundleRun.testSurfaceBundleId,
451
+ testSurfaceBundleRunId: contextForFullPass.bundleRun.id,
452
+ surfaces: allCreates,
453
+ reconcileOnly: allReconciles,
454
+ });
455
+ // Emit events for all created surfaces
456
+ for (const item of batchResult.results) {
457
+ contextForFullPass.events.onTestSurfaceCreated({
458
+ surfaceId: item.surface.id,
459
+ title: item.surface.title,
460
+ });
461
+ }
462
+ }
463
+ // Navigation calls API directly (no surface creation, no reconcile)
441
464
  await generateNavigationTestInteractions(this, contextForFullPass);
442
465
  }
443
466
  async reconcileGeneratedSurfaceElements(context, params) {
@@ -705,19 +728,20 @@ export class PageAnalyzer {
705
728
  .join("||");
706
729
  }
707
730
  async ensureTargetPageState(context) {
708
- const scaffoldIdsBySelector = await this.ensureScaffolds(context);
709
- for (const item of context.actionableItems) {
710
- if (!item.selector)
711
- continue;
712
- const scaffoldSelector = context.scaffoldSelectorByItemSelector[item.selector] ?? null;
713
- if (!scaffoldSelector)
714
- continue;
715
- const scaffoldId = scaffoldIdsBySelector.get(scaffoldSelector);
716
- if (scaffoldId) {
717
- item.scaffoldId = scaffoldId;
718
- }
719
- }
731
+ // Fast path: page state already known — just link scaffolds
720
732
  if (context.currentPageStateId > 0) {
733
+ const scaffoldIdsBySelector = await this.ensureScaffolds(context);
734
+ for (const item of context.actionableItems) {
735
+ if (!item.selector)
736
+ continue;
737
+ const scaffoldSelector = context.scaffoldSelectorByItemSelector[item.selector] ?? null;
738
+ if (!scaffoldSelector)
739
+ continue;
740
+ const scaffoldId = scaffoldIdsBySelector.get(scaffoldSelector);
741
+ if (scaffoldId) {
742
+ item.scaffoldId = scaffoldId;
743
+ }
744
+ }
721
745
  if (scaffoldIdsBySelector.size > 0) {
722
746
  await context.api.linkPageStateScaffolds(context.currentPageStateId, Array.from(new Set(scaffoldIdsBySelector.values())));
723
747
  }
@@ -728,78 +752,68 @@ export class PageAnalyzer {
728
752
  await this.ensureStoredForms(context.currentPageStateId, context);
729
753
  return context.currentPageStateId;
730
754
  }
755
+ // Combined endpoint: scaffolds + match + create in one round-trip
731
756
  const hashes = await computeHashes(context.html, context.actionableItems);
732
- const existing = await context.api.findMatchingPageState(context.pageId, hashes, context.sizeClass);
733
- if (existing) {
734
- if (scaffoldIdsBySelector.size > 0) {
735
- await context.api.linkPageStateScaffolds(existing.id, Array.from(new Set(scaffoldIdsBySelector.values())));
736
- }
737
- context.events.onPageStateCreated({
738
- pageStateId: existing.id,
739
- pageId: context.pageId,
740
- });
741
- await this.ensureStoredForms(existing.id, context);
742
- return existing.id;
743
- }
744
- // Fallback: match by content minus scaffolds (e.g., cookie banner
745
- // presence/absence should not create a different page state)
757
+ let fixedBodyHash;
746
758
  if (context.scaffolds.length > 0) {
747
759
  const body = getBody(context.html);
748
760
  const { contentBody } = getContentBody(body, context.scaffolds);
749
- const contentBodyHash = await sha256(normalizeHtml(contentBody));
750
- const existingByContent = await context.api.findMatchingPageStateByContentBody(context.pageId, contentBodyHash, context.sizeClass);
751
- if (existingByContent) {
752
- if (scaffoldIdsBySelector.size > 0) {
753
- await context.api.linkPageStateScaffolds(existingByContent.id, Array.from(new Set(scaffoldIdsBySelector.values())));
754
- }
755
- context.events.onPageStateCreated({
756
- pageStateId: existingByContent.id,
757
- pageId: context.pageId,
758
- });
759
- await this.ensureStoredForms(existingByContent.id, context);
760
- return existingByContent.id;
761
- }
761
+ fixedBodyHash = await sha256(normalizeHtml(contentBody));
762
762
  }
763
- const contentHash = createHash("sha256").update(context.html).digest("hex");
764
- const contentElement = await context.api.findOrCreateHtmlElement(context.html, contentHash);
765
- await context.api.insertActionableItems(contentElement.id, context.actionableItems);
766
- const pageState = await context.api.createPageState({
767
- pageId: context.pageId,
763
+ const result = await context.api.ensurePageStateCombined({
764
+ pageId: context.pageId > 0 ? context.pageId : undefined,
765
+ relativePath: context.pageId === 0 ? context.currentPath : undefined,
766
+ runnerId: context.runnerId,
767
+ testEnvironmentId: context.testEnvironmentId,
768
768
  sizeClass: context.sizeClass,
769
- hashes,
770
- contentText: htmlToMarkdown(context.html).slice(0, 5000),
771
- contentHtmlElementId: contentElement.id,
772
769
  screenshotPath: context.screenshotPath,
770
+ html: context.html,
771
+ contentText: htmlToMarkdown(context.html).slice(0, 5000),
772
+ hashes,
773
+ fixedBodyHash,
774
+ actionableItems: context.actionableItems,
775
+ scaffolds: context.scaffolds.map(scaffold => ({
776
+ type: scaffold.type,
777
+ html: scaffold.outerHtml,
778
+ hash: scaffold.hash,
779
+ selector: scaffold.selector,
780
+ })),
781
+ scaffoldSelectorByItemSelector: context.scaffoldSelectorByItemSelector,
773
782
  });
774
- // Store content-body hash (HTML minus scaffolds) for fallback matching
775
- if (context.scaffolds.length > 0) {
776
- const body = getBody(context.html);
777
- const { contentBody } = getContentBody(body, context.scaffolds);
778
- const contentBodyHash = await sha256(normalizeHtml(contentBody));
779
- try {
780
- await context.api.updatePageStateDecomposedHashes(pageState.id, {
781
- fixedBodyHash: contentBodyHash,
782
- scaffoldsHash: "",
783
- patternsHash: "",
784
- });
783
+ // Part A: server resolved the page update context
784
+ if (result.pageId && context.pageId === 0) {
785
+ context.pageId = result.pageId;
786
+ context.pageRequiresLogin = result.requiresLogin;
787
+ }
788
+ // Map scaffold IDs back to in-memory actionable items for generators
789
+ for (const item of context.actionableItems) {
790
+ if (!item.selector)
791
+ continue;
792
+ const scaffoldSelector = context.scaffoldSelectorByItemSelector[item.selector] ?? null;
793
+ if (!scaffoldSelector)
794
+ continue;
795
+ const scaffoldId = result.scaffoldIdsBySelector[scaffoldSelector];
796
+ if (scaffoldId) {
797
+ item.scaffoldId = scaffoldId;
785
798
  }
786
- catch (err) {
787
- // Best effort decomposed hashes are optional
788
- logAnalyzer("decomposed-hashes:failed", {
789
- pageStateId: pageState.id,
790
- error: err instanceof Error ? err.message : String(err),
791
- });
799
+ }
800
+ // Update scaffold screenshot if missing (best-effort, parallel)
801
+ if (context.screenshotPath && context.scaffolds.length > 0) {
802
+ for (const scaffold of context.scaffolds) {
803
+ const scaffoldId = result.scaffoldIdsBySelector[scaffold.selector];
804
+ if (scaffoldId) {
805
+ context.api
806
+ .updateScaffoldScreenshot(scaffoldId, context.screenshotPath)
807
+ .catch(() => { });
808
+ }
792
809
  }
793
810
  }
794
811
  context.events.onPageStateCreated({
795
- pageStateId: pageState.id,
796
- pageId: context.pageId,
812
+ pageStateId: result.pageStateId,
813
+ pageId: result.pageId ?? context.pageId,
797
814
  });
798
- if (scaffoldIdsBySelector.size > 0) {
799
- await context.api.linkPageStateScaffolds(pageState.id, Array.from(new Set(scaffoldIdsBySelector.values())));
800
- }
801
- await this.ensureStoredForms(pageState.id, context);
802
- return pageState.id;
815
+ await this.ensureStoredForms(result.pageStateId, context);
816
+ return result.pageStateId;
803
817
  }
804
818
  async ensureScaffolds(context) {
805
819
  const scaffoldIdsBySelector = new Map();