@elliemae/encw-leak-runner 1.0.2 → 1.0.4

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 (135) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +34 -17
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/bin/leak-runner.js +80 -9
  5. package/dist/cjs/cli/commands/runCommand.js +7 -2
  6. package/dist/cjs/config/requiredEnvParams.js +14 -2
  7. package/dist/cjs/config/runnerConfigLoader.js +6 -3
  8. package/dist/cjs/config/runnerConfigSchema.js +5 -1
  9. package/dist/cjs/config/unknownEnvError.js +36 -0
  10. package/dist/cjs/index.js +1 -0
  11. package/dist/cjs/runner/scenarioRunner.js +9 -1
  12. package/dist/cjs/scenarios/one-admin/export-navigation.scenario.js +10 -1
  13. package/dist/cjs/scenarios/one-admin/page-models/AdminLandingPageModel.js +42 -0
  14. package/dist/cjs/scenarios/one-admin/page-models/index.js +2 -0
  15. package/dist/esm/cli/commands/runCommand.js +11 -3
  16. package/dist/esm/config/requiredEnvParams.js +14 -2
  17. package/dist/esm/config/runnerConfigLoader.js +6 -3
  18. package/dist/esm/config/runnerConfigSchema.js +5 -1
  19. package/dist/esm/config/unknownEnvError.js +16 -0
  20. package/dist/esm/index.js +3 -1
  21. package/dist/esm/runner/scenarioRunner.js +9 -1
  22. package/dist/esm/scenarios/one-admin/export-navigation.scenario.js +11 -1
  23. package/dist/esm/scenarios/one-admin/page-models/AdminLandingPageModel.js +22 -0
  24. package/dist/esm/scenarios/one-admin/page-models/index.js +2 -0
  25. package/dist/types/lib/config/requiredEnvParams.d.ts +4 -1
  26. package/dist/types/lib/config/runnerConfigLoader.d.ts +1 -0
  27. package/dist/types/lib/config/runnerConfigSchema.d.ts +7 -0
  28. package/dist/types/lib/config/unknownEnvError.d.ts +5 -0
  29. package/dist/types/lib/index.d.ts +1 -1
  30. package/dist/types/lib/runner/aiEnhancementStep.d.ts +4 -3
  31. package/dist/types/lib/scenarios/one-admin/page-models/AdminLandingPageModel.d.ts +8 -0
  32. package/dist/types/lib/scenarios/one-admin/page-models/index.d.ts +1 -0
  33. package/dist/types/lib/types/scenario.d.ts +1 -0
  34. package/leak-runner.config.json +18 -0
  35. package/leak-runner.schema.json +5 -0
  36. package/lib/cli/commands/runCommand.ts +15 -2
  37. package/lib/config/requiredEnvParams.ts +15 -2
  38. package/lib/config/runnerConfigLoader.ts +12 -5
  39. package/lib/config/runnerConfigSchema.ts +4 -0
  40. package/lib/config/tests/fileConfigSource.test.ts +26 -0
  41. package/lib/config/tests/requiredEnvParams.test.ts +90 -2
  42. package/lib/config/tests/runnerConfigLoader.test.ts +30 -0
  43. package/lib/config/unknownEnvError.ts +13 -0
  44. package/lib/index.ts +1 -0
  45. package/lib/runner/aiEnhancementStep.ts +4 -3
  46. package/lib/runner/scenarioRunner.ts +8 -1
  47. package/lib/scenarios/one-admin/export-navigation.scenario.ts +13 -1
  48. package/lib/scenarios/one-admin/page-models/AdminLandingPageModel.ts +24 -0
  49. package/lib/scenarios/one-admin/page-models/index.ts +1 -0
  50. package/lib/types/scenario.ts +1 -0
  51. package/package.json +3 -3
  52. package/reports/analysis/index.html +1 -1
  53. package/reports/analysis/thresholdEvaluator.ts.html +1 -1
  54. package/reports/browser/iframeHeapProfiler.ts.html +1 -1
  55. package/reports/browser/index.html +1 -1
  56. package/reports/cli/commands/index.html +3 -3
  57. package/reports/cli/commands/listCommand.ts.html +1 -1
  58. package/reports/cli/commands/runCommand.ts.html +44 -5
  59. package/reports/cli/index.html +1 -1
  60. package/reports/cli/index.ts.html +1 -1
  61. package/reports/config/index.html +36 -21
  62. package/reports/config/missingRequiredParamError.ts.html +4 -4
  63. package/reports/config/requiredEnvParams.ts.html +67 -28
  64. package/reports/config/runnerConfigLoader.ts.html +38 -17
  65. package/reports/config/runnerConfigSchema.ts.html +18 -6
  66. package/reports/config/sources/cliOverrideConfigSource.ts.html +1 -1
  67. package/reports/config/sources/configSource.ts.html +2 -2
  68. package/reports/config/sources/envVarConfigSource.ts.html +1 -1
  69. package/reports/config/sources/fileConfigSource.ts.html +13 -13
  70. package/reports/config/sources/index.html +1 -1
  71. package/reports/config/unknownEnvError.ts.html +124 -0
  72. package/reports/index.html +35 -35
  73. package/reports/lcov-report/analysis/index.html +1 -1
  74. package/reports/lcov-report/analysis/thresholdEvaluator.ts.html +1 -1
  75. package/reports/lcov-report/browser/iframeHeapProfiler.ts.html +1 -1
  76. package/reports/lcov-report/browser/index.html +1 -1
  77. package/reports/lcov-report/cli/commands/index.html +3 -3
  78. package/reports/lcov-report/cli/commands/listCommand.ts.html +1 -1
  79. package/reports/lcov-report/cli/commands/runCommand.ts.html +44 -5
  80. package/reports/lcov-report/cli/index.html +1 -1
  81. package/reports/lcov-report/cli/index.ts.html +1 -1
  82. package/reports/lcov-report/config/index.html +36 -21
  83. package/reports/lcov-report/config/missingRequiredParamError.ts.html +4 -4
  84. package/reports/lcov-report/config/requiredEnvParams.ts.html +67 -28
  85. package/reports/lcov-report/config/runnerConfigLoader.ts.html +38 -17
  86. package/reports/lcov-report/config/runnerConfigSchema.ts.html +18 -6
  87. package/reports/lcov-report/config/sources/cliOverrideConfigSource.ts.html +1 -1
  88. package/reports/lcov-report/config/sources/configSource.ts.html +2 -2
  89. package/reports/lcov-report/config/sources/envVarConfigSource.ts.html +1 -1
  90. package/reports/lcov-report/config/sources/fileConfigSource.ts.html +13 -13
  91. package/reports/lcov-report/config/sources/index.html +1 -1
  92. package/reports/lcov-report/config/unknownEnvError.ts.html +124 -0
  93. package/reports/lcov-report/index.html +35 -35
  94. package/reports/lcov-report/registry/index.html +1 -1
  95. package/reports/lcov-report/registry/scenarioRegistry.ts.html +1 -1
  96. package/reports/lcov-report/reporting/consoleReporter.ts.html +1 -1
  97. package/reports/lcov-report/reporting/index.html +1 -1
  98. package/reports/lcov-report/reporting/junitReporter.ts.html +1 -1
  99. package/reports/lcov-report/runner/aiEnhancementStep.ts.html +8 -5
  100. package/reports/lcov-report/runner/batchRunner.ts.html +1 -1
  101. package/reports/lcov-report/runner/index.html +15 -15
  102. package/reports/lcov-report/runner/scenarioRunner.ts.html +32 -11
  103. package/reports/lcov-report/scenarios/index.html +1 -1
  104. package/reports/lcov-report/scenarios/index.ts.html +1 -1
  105. package/reports/lcov-report/scenarios/one-admin/export-navigation.scenario.ts.html +44 -8
  106. package/reports/lcov-report/scenarios/one-admin/index.html +11 -11
  107. package/reports/lcov-report/scenarios/one-admin/index.ts.html +1 -1
  108. package/reports/lcov-report/scenarios/one-admin/page-models/AdminLandingPageModel.ts.html +157 -0
  109. package/reports/lcov-report/scenarios/one-admin/page-models/ExportPageModel.ts.html +1 -1
  110. package/reports/lcov-report/scenarios/one-admin/page-models/SelectSettingsPageModel.ts.html +1 -1
  111. package/reports/lcov-report/scenarios/one-admin/page-models/index.html +19 -4
  112. package/reports/lcov-report/types/config.ts.html +1 -1
  113. package/reports/lcov-report/types/index.html +1 -1
  114. package/reports/lcov.info +302 -230
  115. package/reports/registry/index.html +1 -1
  116. package/reports/registry/scenarioRegistry.ts.html +1 -1
  117. package/reports/reporting/consoleReporter.ts.html +1 -1
  118. package/reports/reporting/index.html +1 -1
  119. package/reports/reporting/junitReporter.ts.html +1 -1
  120. package/reports/runner/aiEnhancementStep.ts.html +8 -5
  121. package/reports/runner/batchRunner.ts.html +1 -1
  122. package/reports/runner/index.html +15 -15
  123. package/reports/runner/scenarioRunner.ts.html +32 -11
  124. package/reports/scenarios/index.html +1 -1
  125. package/reports/scenarios/index.ts.html +1 -1
  126. package/reports/scenarios/one-admin/export-navigation.scenario.ts.html +44 -8
  127. package/reports/scenarios/one-admin/index.html +11 -11
  128. package/reports/scenarios/one-admin/index.ts.html +1 -1
  129. package/reports/scenarios/one-admin/page-models/AdminLandingPageModel.ts.html +157 -0
  130. package/reports/scenarios/one-admin/page-models/ExportPageModel.ts.html +1 -1
  131. package/reports/scenarios/one-admin/page-models/SelectSettingsPageModel.ts.html +1 -1
  132. package/reports/scenarios/one-admin/page-models/index.html +19 -4
  133. package/reports/types/config.ts.html +1 -1
  134. package/reports/types/index.html +1 -1
  135. package/test-report.xml +75 -62
@@ -140,6 +140,7 @@ var ScenarioRunner = class {
140
140
  scenario,
141
141
  snapshotsDir
142
142
  );
143
+ if (scenario.setup) await scenario.setup(page, frame);
143
144
  paths.before = await profiler.captureSnapshot("before");
144
145
  await this.repeatScenarioActions(scenario, page, frame);
145
146
  await forceGarbageCollection(page);
@@ -156,12 +157,19 @@ var ScenarioRunner = class {
156
157
  const auth = new AuthManager();
157
158
  const pageSetup = new PageSetup();
158
159
  await pageSetup.apply(page);
160
+ await page.unroute(PageSetup.BLOCKED_PATTERN);
159
161
  await auth.login(page, {
160
162
  username: this.config.env.userId,
161
163
  password: this.config.env.password,
162
164
  instanceId: this.config.env.instanceId
163
165
  });
164
- await page.goto(scenario.url());
166
+ process.stderr.write(`[scenarioRunner] post-login URL = ${page.url()}
167
+ `);
168
+ await page.waitForURL(`**${scenario.url()}**`, { timeout: 3e4 });
169
+ process.stderr.write(
170
+ `[scenarioRunner] settled URL before iframe = ${page.url()}
171
+ `
172
+ );
165
173
  const frame = await resolveIframe(page, scenario.microappSelector);
166
174
  fs.mkdirSync(snapshotsDir, { recursive: true });
167
175
  const profiler = new IframeHeapProfiler(page, frame, snapshotsDir);
@@ -403,9 +411,12 @@ var aiConfigFileSchema = z.object({
403
411
  model: z.string().min(1),
404
412
  temperature: z.number().min(0).max(2)
405
413
  }).partial();
414
+ var envsSchema = z.record(z.string().min(1));
406
415
  var runnerConfigFileSchema = z.object({
416
+ $schema: z.string().optional(),
407
417
  runner: runnerOptionsSchema,
408
- ai: aiConfigFileSchema
418
+ ai: aiConfigFileSchema,
419
+ envs: envsSchema
409
420
  }).partial().strict();
410
421
 
411
422
  // lib/config/sources/fileConfigSource.ts
@@ -469,7 +480,8 @@ function applySource(acc, source) {
469
480
  const payload = source.load();
470
481
  return {
471
482
  runner: payload.runner ? applyRunnerPayload(acc.runner, payload.runner) : acc.runner,
472
- ai: payload.ai ? applyAiPayload(acc.ai, payload.ai) : acc.ai
483
+ ai: payload.ai ? applyAiPayload(acc.ai, payload.ai) : acc.ai,
484
+ envs: payload.envs ? { ...acc.envs, ...payload.envs } : acc.envs
473
485
  };
474
486
  }
475
487
  var RunnerConfigLoader = class {
@@ -488,14 +500,16 @@ var RunnerConfigLoader = class {
488
500
  aiEnabled: BUILT_IN_DEFAULTS.ai.enabled ?? false,
489
501
  aiModel: BUILT_IN_DEFAULTS.ai.model ?? "Claude3.7",
490
502
  aiTemperature: BUILT_IN_DEFAULTS.ai.temperature ?? 0.3
491
- }
503
+ },
504
+ envs: {}
492
505
  };
493
506
  const resolved = ordered.reduce(applySource, initial);
494
507
  return {
495
508
  runner: resolved.runner,
496
509
  aiEnabled: resolved.ai.aiEnabled,
497
510
  aiModel: resolved.ai.aiModel,
498
- aiTemperature: resolved.ai.aiTemperature
511
+ aiTemperature: resolved.ai.aiTemperature,
512
+ envs: resolved.envs
499
513
  };
500
514
  }
501
515
  };
@@ -512,6 +526,19 @@ Provide via CLI flag (--base-url / --instance-id / --user-id) or env var (BASE_U
512
526
  }
513
527
  };
514
528
 
529
+ // lib/config/unknownEnvError.ts
530
+ var UnknownEnvError = class extends Error {
531
+ constructor(name, knownEnvs) {
532
+ const knownList = knownEnvs.length > 0 ? knownEnvs.join(", ") : "(none defined)";
533
+ super(
534
+ `Unknown env "${name}". Known envs in leak-runner.config.json: ${knownList}.`
535
+ );
536
+ this.name = name;
537
+ this.knownEnvs = knownEnvs;
538
+ this.name = "UnknownEnvError";
539
+ }
540
+ };
541
+
515
542
  // lib/config/requiredEnvParams.ts
516
543
  var RequiredEnvParamsResolver = class {
517
544
  resolve(cliOpts) {
@@ -524,8 +551,18 @@ var RequiredEnvParamsResolver = class {
524
551
  if (missing.length > 0) throw new MissingRequiredParamError(missing);
525
552
  return params;
526
553
  }
554
+ // Precedence: --base-url > BASE_URL env > --env / ENCW_ENV map lookup.
555
+ // Throws UnknownEnvError when an env name is supplied but not in the map,
556
+ // so a typo fails loudly instead of silently falling through to "missing baseUrl".
527
557
  resolveBaseUrl(cliOpts) {
528
- return cliOpts.baseUrl || process.env.BASE_URL || "";
558
+ if (cliOpts.baseUrl) return cliOpts.baseUrl;
559
+ if (process.env.BASE_URL) return process.env.BASE_URL;
560
+ const envName = cliOpts.env || process.env.ENCW_ENV;
561
+ if (!envName) return "";
562
+ const envs = cliOpts.envs ?? {};
563
+ const fromMap = envs[envName];
564
+ if (!fromMap) throw new UnknownEnvError(envName, Object.keys(envs));
565
+ return fromMap;
529
566
  }
530
567
  resolveInstanceId(cliOpts) {
531
568
  return cliOpts.instanceId || process.env.ENCW_INSTANCE_ID || "";
@@ -551,7 +588,10 @@ var RunCommand = class {
551
588
  register(program, deps) {
552
589
  program.command("run [key]").description(
553
590
  "Run a scenario by `<microapp>/<id>`, or use --all / --tag to run multiple"
554
- ).option("--all", "Run all registered scenarios").option("--tag <tag>", "Run all scenarios matching a tag").option("--base-url <url>", "Override base URL").option("--instance-id <id>", "Override instance ID").option("--user-id <id>", "Override user ID").option("--output-dir <dir>", "Override output directory").option(
591
+ ).option("--all", "Run all registered scenarios").option("--tag <tag>", "Run all scenarios matching a tag").option("--base-url <url>", "Override base URL").option(
592
+ "--env <name>",
593
+ "Select a named environment from leak-runner.config.json envs (also reads ENCW_ENV)"
594
+ ).option("--instance-id <id>", "Override instance ID").option("--user-id <id>", "Override user ID").option("--output-dir <dir>", "Override output directory").option(
555
595
  "--top-n <n>",
556
596
  "Override top-N for heap comparison",
557
597
  (v) => parseInt(v, 10)
@@ -568,7 +608,7 @@ var RunCommand = class {
568
608
  await new JunitReporter().write(summary, config.runner.outputDir);
569
609
  process.exit(summary.failCount > 0 ? 1 : 0);
570
610
  } catch (err) {
571
- if (err instanceof MissingRequiredParamError) {
611
+ if (err instanceof MissingRequiredParamError || err instanceof UnknownEnvError) {
572
612
  process.stderr.write(`Configuration error: ${err.message}
573
613
  `);
574
614
  process.exit(2);
@@ -593,6 +633,8 @@ var RunCommand = class {
593
633
  const resolved = loader.resolveOptions();
594
634
  const env = deps.envParams.resolve({
595
635
  baseUrl: options.baseUrl,
636
+ env: options.env,
637
+ envs: resolved.envs,
596
638
  instanceId: options.instanceId,
597
639
  userId: options.userId
598
640
  });
@@ -684,6 +726,26 @@ var ScenarioRegistry = class {
684
726
  }
685
727
  };
686
728
 
729
+ // lib/scenarios/one-admin/page-models/AdminLandingPageModel.ts
730
+ var AdminLandingPageModel = class {
731
+ constructor(frame) {
732
+ this.frame = frame;
733
+ }
734
+ get oneAdminConsoleOption() {
735
+ return this.frame.getByText("ONE ADMIN CONSOLE");
736
+ }
737
+ async clickOneAdminConsole() {
738
+ await this.oneAdminConsoleOption.click();
739
+ }
740
+ static async acceptCookiesIfShown(page) {
741
+ try {
742
+ await page.locator("#onetrust-group-container").waitFor({ state: "visible", timeout: 5e3 });
743
+ await page.locator("button", { hasText: "Accept All Cookies" }).click();
744
+ } catch {
745
+ }
746
+ }
747
+ };
748
+
687
749
  // lib/scenarios/one-admin/page-models/SelectSettingsPageModel.ts
688
750
  var SelectSettingsPageModel = class {
689
751
  constructor(frame) {
@@ -737,8 +799,17 @@ var exportNavigationScenario = {
737
799
  description: "Navigate to export page and come back - verify iframe GC",
738
800
  tags: ["critical"],
739
801
  microappSelector: "iframe#pui-iframe-container-emAdminUI",
740
- url: () => "/admin/oneadmin/migrate",
802
+ url: () => "/admin",
803
+ async setup(page) {
804
+ await AdminLandingPageModel.acceptCookiesIfShown(page);
805
+ },
741
806
  async action(page, frame) {
807
+ const path3 = new URL(page.url()).pathname.replace(/\/$/, "");
808
+ if (path3 === "/admin") {
809
+ const adminLanding = new AdminLandingPageModel(frame);
810
+ await adminLanding.oneAdminConsoleOption.waitFor({ state: "visible" });
811
+ await adminLanding.clickOneAdminConsole();
812
+ }
742
813
  const settings = new SelectSettingsPageModel(frame);
743
814
  await settings.container.waitFor({ state: "visible" });
744
815
  await settings.expandTreeItem("eFolder");
@@ -33,7 +33,10 @@ class RunCommand {
33
33
  register(program, deps) {
34
34
  program.command("run [key]").description(
35
35
  "Run a scenario by `<microapp>/<id>`, or use --all / --tag to run multiple"
36
- ).option("--all", "Run all registered scenarios").option("--tag <tag>", "Run all scenarios matching a tag").option("--base-url <url>", "Override base URL").option("--instance-id <id>", "Override instance ID").option("--user-id <id>", "Override user ID").option("--output-dir <dir>", "Override output directory").option(
36
+ ).option("--all", "Run all registered scenarios").option("--tag <tag>", "Run all scenarios matching a tag").option("--base-url <url>", "Override base URL").option(
37
+ "--env <name>",
38
+ "Select a named environment from leak-runner.config.json envs (also reads ENCW_ENV)"
39
+ ).option("--instance-id <id>", "Override instance ID").option("--user-id <id>", "Override user ID").option("--output-dir <dir>", "Override output directory").option(
37
40
  "--top-n <n>",
38
41
  "Override top-N for heap comparison",
39
42
  (v) => parseInt(v, 10)
@@ -50,7 +53,7 @@ class RunCommand {
50
53
  await new import_junitReporter.JunitReporter().write(summary, config.runner.outputDir);
51
54
  process.exit(summary.failCount > 0 ? 1 : 0);
52
55
  } catch (err) {
53
- if (err instanceof import_requiredEnvParams.MissingRequiredParamError) {
56
+ if (err instanceof import_requiredEnvParams.MissingRequiredParamError || err instanceof import_requiredEnvParams.UnknownEnvError) {
54
57
  process.stderr.write(`Configuration error: ${err.message}
55
58
  `);
56
59
  process.exit(2);
@@ -75,6 +78,8 @@ class RunCommand {
75
78
  const resolved = loader.resolveOptions();
76
79
  const env = deps.envParams.resolve({
77
80
  baseUrl: options.baseUrl,
81
+ env: options.env,
82
+ envs: resolved.envs,
78
83
  instanceId: options.instanceId,
79
84
  userId: options.userId
80
85
  });
@@ -19,10 +19,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var requiredEnvParams_exports = {};
20
20
  __export(requiredEnvParams_exports, {
21
21
  MissingRequiredParamError: () => import_missingRequiredParamError.MissingRequiredParamError,
22
- RequiredEnvParamsResolver: () => RequiredEnvParamsResolver
22
+ RequiredEnvParamsResolver: () => RequiredEnvParamsResolver,
23
+ UnknownEnvError: () => import_unknownEnvError.UnknownEnvError
23
24
  });
24
25
  module.exports = __toCommonJS(requiredEnvParams_exports);
25
26
  var import_missingRequiredParamError = require("./missingRequiredParamError.js");
27
+ var import_unknownEnvError = require("./unknownEnvError.js");
26
28
  class RequiredEnvParamsResolver {
27
29
  resolve(cliOpts) {
28
30
  const baseUrl = this.resolveBaseUrl(cliOpts);
@@ -34,8 +36,18 @@ class RequiredEnvParamsResolver {
34
36
  if (missing.length > 0) throw new import_missingRequiredParamError.MissingRequiredParamError(missing);
35
37
  return params;
36
38
  }
39
+ // Precedence: --base-url > BASE_URL env > --env / ENCW_ENV map lookup.
40
+ // Throws UnknownEnvError when an env name is supplied but not in the map,
41
+ // so a typo fails loudly instead of silently falling through to "missing baseUrl".
37
42
  resolveBaseUrl(cliOpts) {
38
- return cliOpts.baseUrl || process.env.BASE_URL || "";
43
+ if (cliOpts.baseUrl) return cliOpts.baseUrl;
44
+ if (process.env.BASE_URL) return process.env.BASE_URL;
45
+ const envName = cliOpts.env || process.env.ENCW_ENV;
46
+ if (!envName) return "";
47
+ const envs = cliOpts.envs ?? {};
48
+ const fromMap = envs[envName];
49
+ if (!fromMap) throw new import_unknownEnvError.UnknownEnvError(envName, Object.keys(envs));
50
+ return fromMap;
39
51
  }
40
52
  resolveInstanceId(cliOpts) {
41
53
  return cliOpts.instanceId || process.env.ENCW_INSTANCE_ID || "";
@@ -40,7 +40,8 @@ function applySource(acc, source) {
40
40
  const payload = source.load();
41
41
  return {
42
42
  runner: payload.runner ? applyRunnerPayload(acc.runner, payload.runner) : acc.runner,
43
- ai: payload.ai ? applyAiPayload(acc.ai, payload.ai) : acc.ai
43
+ ai: payload.ai ? applyAiPayload(acc.ai, payload.ai) : acc.ai,
44
+ envs: payload.envs ? { ...acc.envs, ...payload.envs } : acc.envs
44
45
  };
45
46
  }
46
47
  class RunnerConfigLoader {
@@ -60,14 +61,16 @@ class RunnerConfigLoader {
60
61
  aiEnabled: import_configSource.BUILT_IN_DEFAULTS.ai.enabled ?? false,
61
62
  aiModel: import_configSource.BUILT_IN_DEFAULTS.ai.model ?? "Claude3.7",
62
63
  aiTemperature: import_configSource.BUILT_IN_DEFAULTS.ai.temperature ?? 0.3
63
- }
64
+ },
65
+ envs: {}
64
66
  };
65
67
  const resolved = ordered.reduce(applySource, initial);
66
68
  return {
67
69
  runner: resolved.runner,
68
70
  aiEnabled: resolved.ai.aiEnabled,
69
71
  aiModel: resolved.ai.aiModel,
70
- aiTemperature: resolved.ai.aiTemperature
72
+ aiTemperature: resolved.ai.aiTemperature,
73
+ envs: resolved.envs
71
74
  };
72
75
  }
73
76
  }
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var runnerConfigSchema_exports = {};
20
20
  __export(runnerConfigSchema_exports, {
21
21
  aiConfigFileSchema: () => aiConfigFileSchema,
22
+ envsSchema: () => envsSchema,
22
23
  runnerConfigFileSchema: () => runnerConfigFileSchema,
23
24
  runnerOptionsSchema: () => runnerOptionsSchema
24
25
  });
@@ -34,7 +35,10 @@ const aiConfigFileSchema = import_zod.z.object({
34
35
  model: import_zod.z.string().min(1),
35
36
  temperature: import_zod.z.number().min(0).max(2)
36
37
  }).partial();
38
+ const envsSchema = import_zod.z.record(import_zod.z.string().min(1));
37
39
  const runnerConfigFileSchema = import_zod.z.object({
40
+ $schema: import_zod.z.string().optional(),
38
41
  runner: runnerOptionsSchema,
39
- ai: aiConfigFileSchema
42
+ ai: aiConfigFileSchema,
43
+ envs: envsSchema
40
44
  }).partial().strict();
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var unknownEnvError_exports = {};
20
+ __export(unknownEnvError_exports, {
21
+ UnknownEnvError: () => UnknownEnvError
22
+ });
23
+ module.exports = __toCommonJS(unknownEnvError_exports);
24
+ class UnknownEnvError extends Error {
25
+ constructor(name, knownEnvs) {
26
+ const knownList = knownEnvs.length > 0 ? knownEnvs.join(", ") : "(none defined)";
27
+ super(
28
+ `Unknown env "${name}". Known envs in leak-runner.config.json: ${knownList}.`
29
+ );
30
+ this.name = name;
31
+ this.knownEnvs = knownEnvs;
32
+ this.name = "UnknownEnvError";
33
+ }
34
+ name;
35
+ knownEnvs;
36
+ }
package/dist/cjs/index.js CHANGED
@@ -32,6 +32,7 @@ __export(index_exports, {
32
32
  ScenarioRegistry: () => import_scenarioRegistry.ScenarioRegistry,
33
33
  ScenarioRunner: () => import_scenarioRunner.ScenarioRunner,
34
34
  ThresholdEvaluator: () => import_thresholdEvaluator.ThresholdEvaluator,
35
+ UnknownEnvError: () => import_requiredEnvParams.UnknownEnvError,
35
36
  buildProgram: () => import_cli.buildProgram,
36
37
  defaultDeps: () => import_cli.defaultDeps
37
38
  });
@@ -93,6 +93,7 @@ class ScenarioRunner {
93
93
  scenario,
94
94
  snapshotsDir
95
95
  );
96
+ if (scenario.setup) await scenario.setup(page, frame);
96
97
  paths.before = await profiler.captureSnapshot("before");
97
98
  await this.repeatScenarioActions(scenario, page, frame);
98
99
  await forceGarbageCollection(page);
@@ -109,12 +110,19 @@ class ScenarioRunner {
109
110
  const auth = new import_smoked_suite.AuthManager();
110
111
  const pageSetup = new import_smoked_suite.PageSetup();
111
112
  await pageSetup.apply(page);
113
+ await page.unroute(import_smoked_suite.PageSetup.BLOCKED_PATTERN);
112
114
  await auth.login(page, {
113
115
  username: this.config.env.userId,
114
116
  password: this.config.env.password,
115
117
  instanceId: this.config.env.instanceId
116
118
  });
117
- await page.goto(scenario.url());
119
+ process.stderr.write(`[scenarioRunner] post-login URL = ${page.url()}
120
+ `);
121
+ await page.waitForURL(`**${scenario.url()}**`, { timeout: 3e4 });
122
+ process.stderr.write(
123
+ `[scenarioRunner] settled URL before iframe = ${page.url()}
124
+ `
125
+ );
118
126
  const frame = await resolveIframe(page, scenario.microappSelector);
119
127
  import_node_fs.default.mkdirSync(snapshotsDir, { recursive: true });
120
128
  const profiler = new import_iframeHeapProfiler.IframeHeapProfiler(page, frame, snapshotsDir);
@@ -28,8 +28,17 @@ const exportNavigationScenario = {
28
28
  description: "Navigate to export page and come back - verify iframe GC",
29
29
  tags: ["critical"],
30
30
  microappSelector: "iframe#pui-iframe-container-emAdminUI",
31
- url: () => "/admin/oneadmin/migrate",
31
+ url: () => "/admin",
32
+ async setup(page) {
33
+ await import_page_models.AdminLandingPageModel.acceptCookiesIfShown(page);
34
+ },
32
35
  async action(page, frame) {
36
+ const path = new URL(page.url()).pathname.replace(/\/$/, "");
37
+ if (path === "/admin") {
38
+ const adminLanding = new import_page_models.AdminLandingPageModel(frame);
39
+ await adminLanding.oneAdminConsoleOption.waitFor({ state: "visible" });
40
+ await adminLanding.clickOneAdminConsole();
41
+ }
33
42
  const settings = new import_page_models.SelectSettingsPageModel(frame);
34
43
  await settings.container.waitFor({ state: "visible" });
35
44
  await settings.expandTreeItem("eFolder");
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var AdminLandingPageModel_exports = {};
20
+ __export(AdminLandingPageModel_exports, {
21
+ AdminLandingPageModel: () => AdminLandingPageModel
22
+ });
23
+ module.exports = __toCommonJS(AdminLandingPageModel_exports);
24
+ class AdminLandingPageModel {
25
+ constructor(frame) {
26
+ this.frame = frame;
27
+ }
28
+ frame;
29
+ get oneAdminConsoleOption() {
30
+ return this.frame.getByText("ONE ADMIN CONSOLE");
31
+ }
32
+ async clickOneAdminConsole() {
33
+ await this.oneAdminConsoleOption.click();
34
+ }
35
+ static async acceptCookiesIfShown(page) {
36
+ try {
37
+ await page.locator("#onetrust-group-container").waitFor({ state: "visible", timeout: 5e3 });
38
+ await page.locator("button", { hasText: "Accept All Cookies" }).click();
39
+ } catch {
40
+ }
41
+ }
42
+ }
@@ -18,9 +18,11 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var page_models_exports = {};
20
20
  __export(page_models_exports, {
21
+ AdminLandingPageModel: () => import_AdminLandingPageModel.AdminLandingPageModel,
21
22
  ExportPageModel: () => import_ExportPageModel.ExportPageModel,
22
23
  SelectSettingsPageModel: () => import_SelectSettingsPageModel.SelectSettingsPageModel
23
24
  });
24
25
  module.exports = __toCommonJS(page_models_exports);
26
+ var import_AdminLandingPageModel = require("./AdminLandingPageModel.js");
25
27
  var import_SelectSettingsPageModel = require("./SelectSettingsPageModel.js");
26
28
  var import_ExportPageModel = require("./ExportPageModel.js");
@@ -7,12 +7,18 @@ import {
7
7
  import { EnvVarConfigSource } from "../../config/sources/envVarConfigSource.js";
8
8
  import { FileConfigSource } from "../../config/sources/fileConfigSource.js";
9
9
  import { RunnerConfigLoader } from "../../config/runnerConfigLoader.js";
10
- import { MissingRequiredParamError } from "../../config/requiredEnvParams.js";
10
+ import {
11
+ MissingRequiredParamError,
12
+ UnknownEnvError
13
+ } from "../../config/requiredEnvParams.js";
11
14
  class RunCommand {
12
15
  register(program, deps) {
13
16
  program.command("run [key]").description(
14
17
  "Run a scenario by `<microapp>/<id>`, or use --all / --tag to run multiple"
15
- ).option("--all", "Run all registered scenarios").option("--tag <tag>", "Run all scenarios matching a tag").option("--base-url <url>", "Override base URL").option("--instance-id <id>", "Override instance ID").option("--user-id <id>", "Override user ID").option("--output-dir <dir>", "Override output directory").option(
18
+ ).option("--all", "Run all registered scenarios").option("--tag <tag>", "Run all scenarios matching a tag").option("--base-url <url>", "Override base URL").option(
19
+ "--env <name>",
20
+ "Select a named environment from leak-runner.config.json envs (also reads ENCW_ENV)"
21
+ ).option("--instance-id <id>", "Override instance ID").option("--user-id <id>", "Override user ID").option("--output-dir <dir>", "Override output directory").option(
16
22
  "--top-n <n>",
17
23
  "Override top-N for heap comparison",
18
24
  (v) => parseInt(v, 10)
@@ -29,7 +35,7 @@ class RunCommand {
29
35
  await new JunitReporter().write(summary, config.runner.outputDir);
30
36
  process.exit(summary.failCount > 0 ? 1 : 0);
31
37
  } catch (err) {
32
- if (err instanceof MissingRequiredParamError) {
38
+ if (err instanceof MissingRequiredParamError || err instanceof UnknownEnvError) {
33
39
  process.stderr.write(`Configuration error: ${err.message}
34
40
  `);
35
41
  process.exit(2);
@@ -54,6 +60,8 @@ class RunCommand {
54
60
  const resolved = loader.resolveOptions();
55
61
  const env = deps.envParams.resolve({
56
62
  baseUrl: options.baseUrl,
63
+ env: options.env,
64
+ envs: resolved.envs,
57
65
  instanceId: options.instanceId,
58
66
  userId: options.userId
59
67
  });
@@ -1,4 +1,5 @@
1
1
  import { MissingRequiredParamError } from "./missingRequiredParamError.js";
2
+ import { UnknownEnvError } from "./unknownEnvError.js";
2
3
  class RequiredEnvParamsResolver {
3
4
  resolve(cliOpts) {
4
5
  const baseUrl = this.resolveBaseUrl(cliOpts);
@@ -10,8 +11,18 @@ class RequiredEnvParamsResolver {
10
11
  if (missing.length > 0) throw new MissingRequiredParamError(missing);
11
12
  return params;
12
13
  }
14
+ // Precedence: --base-url > BASE_URL env > --env / ENCW_ENV map lookup.
15
+ // Throws UnknownEnvError when an env name is supplied but not in the map,
16
+ // so a typo fails loudly instead of silently falling through to "missing baseUrl".
13
17
  resolveBaseUrl(cliOpts) {
14
- return cliOpts.baseUrl || process.env.BASE_URL || "";
18
+ if (cliOpts.baseUrl) return cliOpts.baseUrl;
19
+ if (process.env.BASE_URL) return process.env.BASE_URL;
20
+ const envName = cliOpts.env || process.env.ENCW_ENV;
21
+ if (!envName) return "";
22
+ const envs = cliOpts.envs ?? {};
23
+ const fromMap = envs[envName];
24
+ if (!fromMap) throw new UnknownEnvError(envName, Object.keys(envs));
25
+ return fromMap;
15
26
  }
16
27
  resolveInstanceId(cliOpts) {
17
28
  return cliOpts.instanceId || process.env.ENCW_INSTANCE_ID || "";
@@ -33,5 +44,6 @@ class RequiredEnvParamsResolver {
33
44
  }
34
45
  export {
35
46
  MissingRequiredParamError,
36
- RequiredEnvParamsResolver
47
+ RequiredEnvParamsResolver,
48
+ UnknownEnvError
37
49
  };
@@ -17,7 +17,8 @@ function applySource(acc, source) {
17
17
  const payload = source.load();
18
18
  return {
19
19
  runner: payload.runner ? applyRunnerPayload(acc.runner, payload.runner) : acc.runner,
20
- ai: payload.ai ? applyAiPayload(acc.ai, payload.ai) : acc.ai
20
+ ai: payload.ai ? applyAiPayload(acc.ai, payload.ai) : acc.ai,
21
+ envs: payload.envs ? { ...acc.envs, ...payload.envs } : acc.envs
21
22
  };
22
23
  }
23
24
  class RunnerConfigLoader {
@@ -37,14 +38,16 @@ class RunnerConfigLoader {
37
38
  aiEnabled: BUILT_IN_DEFAULTS.ai.enabled ?? false,
38
39
  aiModel: BUILT_IN_DEFAULTS.ai.model ?? "Claude3.7",
39
40
  aiTemperature: BUILT_IN_DEFAULTS.ai.temperature ?? 0.3
40
- }
41
+ },
42
+ envs: {}
41
43
  };
42
44
  const resolved = ordered.reduce(applySource, initial);
43
45
  return {
44
46
  runner: resolved.runner,
45
47
  aiEnabled: resolved.ai.aiEnabled,
46
48
  aiModel: resolved.ai.aiModel,
47
- aiTemperature: resolved.ai.aiTemperature
49
+ aiTemperature: resolved.ai.aiTemperature,
50
+ envs: resolved.envs
48
51
  };
49
52
  }
50
53
  }
@@ -9,12 +9,16 @@ const aiConfigFileSchema = z.object({
9
9
  model: z.string().min(1),
10
10
  temperature: z.number().min(0).max(2)
11
11
  }).partial();
12
+ const envsSchema = z.record(z.string().min(1));
12
13
  const runnerConfigFileSchema = z.object({
14
+ $schema: z.string().optional(),
13
15
  runner: runnerOptionsSchema,
14
- ai: aiConfigFileSchema
16
+ ai: aiConfigFileSchema,
17
+ envs: envsSchema
15
18
  }).partial().strict();
16
19
  export {
17
20
  aiConfigFileSchema,
21
+ envsSchema,
18
22
  runnerConfigFileSchema,
19
23
  runnerOptionsSchema
20
24
  };
@@ -0,0 +1,16 @@
1
+ class UnknownEnvError extends Error {
2
+ constructor(name, knownEnvs) {
3
+ const knownList = knownEnvs.length > 0 ? knownEnvs.join(", ") : "(none defined)";
4
+ super(
5
+ `Unknown env "${name}". Known envs in leak-runner.config.json: ${knownList}.`
6
+ );
7
+ this.name = name;
8
+ this.knownEnvs = knownEnvs;
9
+ this.name = "UnknownEnvError";
10
+ }
11
+ name;
12
+ knownEnvs;
13
+ }
14
+ export {
15
+ UnknownEnvError
16
+ };
package/dist/esm/index.js CHANGED
@@ -12,7 +12,8 @@ import { EnvVarConfigSource } from "./config/sources/envVarConfigSource.js";
12
12
  import { CliOverrideConfigSource } from "./config/sources/cliOverrideConfigSource.js";
13
13
  import {
14
14
  RequiredEnvParamsResolver,
15
- MissingRequiredParamError
15
+ MissingRequiredParamError,
16
+ UnknownEnvError
16
17
  } from "./config/requiredEnvParams.js";
17
18
  import { buildProgram, defaultDeps } from "./cli/index.js";
18
19
  export {
@@ -30,6 +31,7 @@ export {
30
31
  ScenarioRegistry,
31
32
  ScenarioRunner,
32
33
  ThresholdEvaluator,
34
+ UnknownEnvError,
33
35
  buildProgram,
34
36
  defaultDeps
35
37
  };
@@ -62,6 +62,7 @@ class ScenarioRunner {
62
62
  scenario,
63
63
  snapshotsDir
64
64
  );
65
+ if (scenario.setup) await scenario.setup(page, frame);
65
66
  paths.before = await profiler.captureSnapshot("before");
66
67
  await this.repeatScenarioActions(scenario, page, frame);
67
68
  await forceGarbageCollection(page);
@@ -78,12 +79,19 @@ class ScenarioRunner {
78
79
  const auth = new AuthManager();
79
80
  const pageSetup = new PageSetup();
80
81
  await pageSetup.apply(page);
82
+ await page.unroute(PageSetup.BLOCKED_PATTERN);
81
83
  await auth.login(page, {
82
84
  username: this.config.env.userId,
83
85
  password: this.config.env.password,
84
86
  instanceId: this.config.env.instanceId
85
87
  });
86
- await page.goto(scenario.url());
88
+ process.stderr.write(`[scenarioRunner] post-login URL = ${page.url()}
89
+ `);
90
+ await page.waitForURL(`**${scenario.url()}**`, { timeout: 3e4 });
91
+ process.stderr.write(
92
+ `[scenarioRunner] settled URL before iframe = ${page.url()}
93
+ `
94
+ );
87
95
  const frame = await resolveIframe(page, scenario.microappSelector);
88
96
  fs.mkdirSync(snapshotsDir, { recursive: true });
89
97
  const profiler = new IframeHeapProfiler(page, frame, snapshotsDir);
@@ -1,4 +1,5 @@
1
1
  import {
2
+ AdminLandingPageModel,
2
3
  SelectSettingsPageModel,
3
4
  ExportPageModel
4
5
  } from "./page-models/index.js";
@@ -8,8 +9,17 @@ const exportNavigationScenario = {
8
9
  description: "Navigate to export page and come back - verify iframe GC",
9
10
  tags: ["critical"],
10
11
  microappSelector: "iframe#pui-iframe-container-emAdminUI",
11
- url: () => "/admin/oneadmin/migrate",
12
+ url: () => "/admin",
13
+ async setup(page) {
14
+ await AdminLandingPageModel.acceptCookiesIfShown(page);
15
+ },
12
16
  async action(page, frame) {
17
+ const path = new URL(page.url()).pathname.replace(/\/$/, "");
18
+ if (path === "/admin") {
19
+ const adminLanding = new AdminLandingPageModel(frame);
20
+ await adminLanding.oneAdminConsoleOption.waitFor({ state: "visible" });
21
+ await adminLanding.clickOneAdminConsole();
22
+ }
13
23
  const settings = new SelectSettingsPageModel(frame);
14
24
  await settings.container.waitFor({ state: "visible" });
15
25
  await settings.expandTreeItem("eFolder");