@elliemae/encw-leak-runner 1.0.3 → 1.0.5

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 (138) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +34 -17
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/bin/leak-runner.js +80 -11
  5. package/dist/cjs/cli/commands/runCommand.js +7 -3
  6. package/dist/cjs/config/missingRequiredParamError.js +1 -1
  7. package/dist/cjs/config/requiredEnvParams.js +13 -2
  8. package/dist/cjs/config/runnerConfigLoader.js +6 -3
  9. package/dist/cjs/config/runnerConfigSchema.js +5 -1
  10. package/dist/cjs/config/unknownEnvError.js +36 -0
  11. package/dist/cjs/index.js +1 -0
  12. package/dist/cjs/runner/scenarioRunner.js +9 -1
  13. package/dist/cjs/scenarios/one-admin/export-navigation.scenario.js +10 -1
  14. package/dist/cjs/scenarios/one-admin/page-models/AdminLandingPageModel.js +42 -0
  15. package/dist/cjs/scenarios/one-admin/page-models/index.js +2 -0
  16. package/dist/esm/cli/commands/runCommand.js +11 -4
  17. package/dist/esm/config/missingRequiredParamError.js +1 -1
  18. package/dist/esm/config/requiredEnvParams.js +13 -2
  19. package/dist/esm/config/runnerConfigLoader.js +6 -3
  20. package/dist/esm/config/runnerConfigSchema.js +5 -1
  21. package/dist/esm/config/unknownEnvError.js +16 -0
  22. package/dist/esm/index.js +3 -1
  23. package/dist/esm/runner/scenarioRunner.js +9 -1
  24. package/dist/esm/scenarios/one-admin/export-navigation.scenario.js +11 -1
  25. package/dist/esm/scenarios/one-admin/page-models/AdminLandingPageModel.js +22 -0
  26. package/dist/esm/scenarios/one-admin/page-models/index.js +2 -0
  27. package/dist/types/lib/config/requiredEnvParams.d.ts +4 -2
  28. package/dist/types/lib/config/runnerConfigLoader.d.ts +1 -0
  29. package/dist/types/lib/config/runnerConfigSchema.d.ts +7 -0
  30. package/dist/types/lib/config/unknownEnvError.d.ts +5 -0
  31. package/dist/types/lib/index.d.ts +1 -1
  32. package/dist/types/lib/runner/aiEnhancementStep.d.ts +4 -3
  33. package/dist/types/lib/scenarios/one-admin/page-models/AdminLandingPageModel.d.ts +8 -0
  34. package/dist/types/lib/scenarios/one-admin/page-models/index.d.ts +1 -0
  35. package/dist/types/lib/types/scenario.d.ts +1 -0
  36. package/leak-runner.config.json +18 -0
  37. package/leak-runner.schema.json +5 -0
  38. package/lib/cli/commands/runCommand.ts +15 -5
  39. package/lib/config/missingRequiredParamError.ts +2 -2
  40. package/lib/config/requiredEnvParams.ts +14 -3
  41. package/lib/config/runnerConfigLoader.ts +12 -5
  42. package/lib/config/runnerConfigSchema.ts +4 -0
  43. package/lib/config/tests/fileConfigSource.test.ts +26 -0
  44. package/lib/config/tests/requiredEnvParams.test.ts +80 -5
  45. package/lib/config/tests/runnerConfigLoader.test.ts +30 -0
  46. package/lib/config/unknownEnvError.ts +13 -0
  47. package/lib/index.ts +1 -0
  48. package/lib/runner/aiEnhancementStep.ts +4 -3
  49. package/lib/runner/scenarioRunner.ts +8 -1
  50. package/lib/scenarios/one-admin/export-navigation.scenario.ts +13 -1
  51. package/lib/scenarios/one-admin/page-models/AdminLandingPageModel.ts +24 -0
  52. package/lib/scenarios/one-admin/page-models/index.ts +1 -0
  53. package/lib/types/scenario.ts +1 -0
  54. package/package.json +3 -3
  55. package/reports/analysis/index.html +1 -1
  56. package/reports/analysis/thresholdEvaluator.ts.html +1 -1
  57. package/reports/browser/iframeHeapProfiler.ts.html +1 -1
  58. package/reports/browser/index.html +1 -1
  59. package/reports/cli/commands/index.html +3 -3
  60. package/reports/cli/commands/listCommand.ts.html +1 -1
  61. package/reports/cli/commands/runCommand.ts.html +38 -8
  62. package/reports/cli/index.html +1 -1
  63. package/reports/cli/index.ts.html +1 -1
  64. package/reports/config/index.html +36 -21
  65. package/reports/config/missingRequiredParamError.ts.html +6 -6
  66. package/reports/config/requiredEnvParams.ts.html +61 -28
  67. package/reports/config/runnerConfigLoader.ts.html +38 -17
  68. package/reports/config/runnerConfigSchema.ts.html +18 -6
  69. package/reports/config/sources/cliOverrideConfigSource.ts.html +1 -1
  70. package/reports/config/sources/configSource.ts.html +2 -2
  71. package/reports/config/sources/envVarConfigSource.ts.html +1 -1
  72. package/reports/config/sources/fileConfigSource.ts.html +13 -13
  73. package/reports/config/sources/index.html +1 -1
  74. package/reports/config/unknownEnvError.ts.html +124 -0
  75. package/reports/index.html +35 -35
  76. package/reports/lcov-report/analysis/index.html +1 -1
  77. package/reports/lcov-report/analysis/thresholdEvaluator.ts.html +1 -1
  78. package/reports/lcov-report/browser/iframeHeapProfiler.ts.html +1 -1
  79. package/reports/lcov-report/browser/index.html +1 -1
  80. package/reports/lcov-report/cli/commands/index.html +3 -3
  81. package/reports/lcov-report/cli/commands/listCommand.ts.html +1 -1
  82. package/reports/lcov-report/cli/commands/runCommand.ts.html +38 -8
  83. package/reports/lcov-report/cli/index.html +1 -1
  84. package/reports/lcov-report/cli/index.ts.html +1 -1
  85. package/reports/lcov-report/config/index.html +36 -21
  86. package/reports/lcov-report/config/missingRequiredParamError.ts.html +6 -6
  87. package/reports/lcov-report/config/requiredEnvParams.ts.html +61 -28
  88. package/reports/lcov-report/config/runnerConfigLoader.ts.html +38 -17
  89. package/reports/lcov-report/config/runnerConfigSchema.ts.html +18 -6
  90. package/reports/lcov-report/config/sources/cliOverrideConfigSource.ts.html +1 -1
  91. package/reports/lcov-report/config/sources/configSource.ts.html +2 -2
  92. package/reports/lcov-report/config/sources/envVarConfigSource.ts.html +1 -1
  93. package/reports/lcov-report/config/sources/fileConfigSource.ts.html +13 -13
  94. package/reports/lcov-report/config/sources/index.html +1 -1
  95. package/reports/lcov-report/config/unknownEnvError.ts.html +124 -0
  96. package/reports/lcov-report/index.html +35 -35
  97. package/reports/lcov-report/registry/index.html +1 -1
  98. package/reports/lcov-report/registry/scenarioRegistry.ts.html +1 -1
  99. package/reports/lcov-report/reporting/consoleReporter.ts.html +1 -1
  100. package/reports/lcov-report/reporting/index.html +1 -1
  101. package/reports/lcov-report/reporting/junitReporter.ts.html +1 -1
  102. package/reports/lcov-report/runner/aiEnhancementStep.ts.html +8 -5
  103. package/reports/lcov-report/runner/batchRunner.ts.html +1 -1
  104. package/reports/lcov-report/runner/index.html +15 -15
  105. package/reports/lcov-report/runner/scenarioRunner.ts.html +32 -11
  106. package/reports/lcov-report/scenarios/index.html +1 -1
  107. package/reports/lcov-report/scenarios/index.ts.html +1 -1
  108. package/reports/lcov-report/scenarios/one-admin/export-navigation.scenario.ts.html +44 -8
  109. package/reports/lcov-report/scenarios/one-admin/index.html +11 -11
  110. package/reports/lcov-report/scenarios/one-admin/index.ts.html +1 -1
  111. package/reports/lcov-report/scenarios/one-admin/page-models/AdminLandingPageModel.ts.html +157 -0
  112. package/reports/lcov-report/scenarios/one-admin/page-models/ExportPageModel.ts.html +1 -1
  113. package/reports/lcov-report/scenarios/one-admin/page-models/SelectSettingsPageModel.ts.html +1 -1
  114. package/reports/lcov-report/scenarios/one-admin/page-models/index.html +19 -4
  115. package/reports/lcov-report/types/config.ts.html +1 -1
  116. package/reports/lcov-report/types/index.html +1 -1
  117. package/reports/lcov.info +301 -231
  118. package/reports/registry/index.html +1 -1
  119. package/reports/registry/scenarioRegistry.ts.html +1 -1
  120. package/reports/reporting/consoleReporter.ts.html +1 -1
  121. package/reports/reporting/index.html +1 -1
  122. package/reports/reporting/junitReporter.ts.html +1 -1
  123. package/reports/runner/aiEnhancementStep.ts.html +8 -5
  124. package/reports/runner/batchRunner.ts.html +1 -1
  125. package/reports/runner/index.html +15 -15
  126. package/reports/runner/scenarioRunner.ts.html +32 -11
  127. package/reports/scenarios/index.html +1 -1
  128. package/reports/scenarios/index.ts.html +1 -1
  129. package/reports/scenarios/one-admin/export-navigation.scenario.ts.html +44 -8
  130. package/reports/scenarios/one-admin/index.html +11 -11
  131. package/reports/scenarios/one-admin/index.ts.html +1 -1
  132. package/reports/scenarios/one-admin/page-models/AdminLandingPageModel.ts.html +157 -0
  133. package/reports/scenarios/one-admin/page-models/ExportPageModel.ts.html +1 -1
  134. package/reports/scenarios/one-admin/page-models/SelectSettingsPageModel.ts.html +1 -1
  135. package/reports/scenarios/one-admin/page-models/index.html +19 -4
  136. package/reports/types/config.ts.html +1 -1
  137. package/reports/types/index.html +1 -1
  138. package/test-report.xml +74 -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
  };
@@ -505,13 +519,26 @@ var MissingRequiredParamError = class extends Error {
505
519
  constructor(missing) {
506
520
  super(
507
521
  `Missing required parameter(s): ${missing.join(", ")}.
508
- Provide via CLI flag (--base-url / --instance-id / --user-id) or env var (BASE_URL / ENCW_INSTANCE_ID / ENCW_USER_ID / ENCW_PASSWORD).`
522
+ Provide via CLI flag (--env / --instance-id / --user-id) or env var (BASE_URL / ENCW_ENV / ENCW_INSTANCE_ID / ENCW_USER_ID / ENCW_PASSWORD).`
509
523
  );
510
524
  this.missing = missing;
511
525
  this.name = "MissingRequiredParamError";
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,17 @@ var RequiredEnvParamsResolver = class {
524
551
  if (missing.length > 0) throw new MissingRequiredParamError(missing);
525
552
  return params;
526
553
  }
554
+ // Precedence: 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 (process.env.BASE_URL) return process.env.BASE_URL;
559
+ const envName = cliOpts.env || process.env.ENCW_ENV;
560
+ if (!envName) return "";
561
+ const envs = cliOpts.envs ?? {};
562
+ const fromMap = envs[envName];
563
+ if (!fromMap) throw new UnknownEnvError(envName, Object.keys(envs));
564
+ return fromMap;
529
565
  }
530
566
  resolveInstanceId(cliOpts) {
531
567
  return cliOpts.instanceId || process.env.ENCW_INSTANCE_ID || "";
@@ -551,7 +587,10 @@ var RunCommand = class {
551
587
  register(program, deps) {
552
588
  program.command("run [key]").description(
553
589
  "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(
590
+ ).option("--all", "Run all registered scenarios").option("--tag <tag>", "Run all scenarios matching a tag").option(
591
+ "--env <name>",
592
+ "Select a named environment from leak-runner.config.json envs (also reads ENCW_ENV)"
593
+ ).option("--instance-id <id>", "Override instance ID").option("--user-id <id>", "Override user ID").option("--output-dir <dir>", "Override output directory").option(
555
594
  "--top-n <n>",
556
595
  "Override top-N for heap comparison",
557
596
  (v) => parseInt(v, 10)
@@ -568,7 +607,7 @@ var RunCommand = class {
568
607
  await new JunitReporter().write(summary, config.runner.outputDir);
569
608
  process.exit(summary.failCount > 0 ? 1 : 0);
570
609
  } catch (err) {
571
- if (err instanceof MissingRequiredParamError) {
610
+ if (err instanceof MissingRequiredParamError || err instanceof UnknownEnvError) {
572
611
  process.stderr.write(`Configuration error: ${err.message}
573
612
  `);
574
613
  process.exit(2);
@@ -592,7 +631,8 @@ var RunCommand = class {
592
631
  ]);
593
632
  const resolved = loader.resolveOptions();
594
633
  const env = deps.envParams.resolve({
595
- baseUrl: options.baseUrl,
634
+ env: options.env,
635
+ envs: resolved.envs,
596
636
  instanceId: options.instanceId,
597
637
  userId: options.userId
598
638
  });
@@ -684,6 +724,26 @@ var ScenarioRegistry = class {
684
724
  }
685
725
  };
686
726
 
727
+ // lib/scenarios/one-admin/page-models/AdminLandingPageModel.ts
728
+ var AdminLandingPageModel = class {
729
+ constructor(frame) {
730
+ this.frame = frame;
731
+ }
732
+ get oneAdminConsoleOption() {
733
+ return this.frame.getByText("ONE ADMIN CONSOLE");
734
+ }
735
+ async clickOneAdminConsole() {
736
+ await this.oneAdminConsoleOption.click();
737
+ }
738
+ static async acceptCookiesIfShown(page) {
739
+ try {
740
+ await page.locator("#onetrust-group-container").waitFor({ state: "visible", timeout: 5e3 });
741
+ await page.locator("button", { hasText: "Accept All Cookies" }).click();
742
+ } catch {
743
+ }
744
+ }
745
+ };
746
+
687
747
  // lib/scenarios/one-admin/page-models/SelectSettingsPageModel.ts
688
748
  var SelectSettingsPageModel = class {
689
749
  constructor(frame) {
@@ -737,8 +797,17 @@ var exportNavigationScenario = {
737
797
  description: "Navigate to export page and come back - verify iframe GC",
738
798
  tags: ["critical"],
739
799
  microappSelector: "iframe#pui-iframe-container-emAdminUI",
740
- url: () => "/admin/oneadmin/migrate",
800
+ url: () => "/admin",
801
+ async setup(page) {
802
+ await AdminLandingPageModel.acceptCookiesIfShown(page);
803
+ },
741
804
  async action(page, frame) {
805
+ const path3 = new URL(page.url()).pathname.replace(/\/$/, "");
806
+ if (path3 === "/admin") {
807
+ const adminLanding = new AdminLandingPageModel(frame);
808
+ await adminLanding.oneAdminConsoleOption.waitFor({ state: "visible" });
809
+ await adminLanding.clickOneAdminConsole();
810
+ }
742
811
  const settings = new SelectSettingsPageModel(frame);
743
812
  await settings.container.waitFor({ state: "visible" });
744
813
  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(
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);
@@ -74,7 +77,8 @@ class RunCommand {
74
77
  ]);
75
78
  const resolved = loader.resolveOptions();
76
79
  const env = deps.envParams.resolve({
77
- baseUrl: options.baseUrl,
80
+ env: options.env,
81
+ envs: resolved.envs,
78
82
  instanceId: options.instanceId,
79
83
  userId: options.userId
80
84
  });
@@ -25,7 +25,7 @@ class MissingRequiredParamError extends Error {
25
25
  constructor(missing) {
26
26
  super(
27
27
  `Missing required parameter(s): ${missing.join(", ")}.
28
- Provide via CLI flag (--base-url / --instance-id / --user-id) or env var (BASE_URL / ENCW_INSTANCE_ID / ENCW_USER_ID / ENCW_PASSWORD).`
28
+ Provide via CLI flag (--env / --instance-id / --user-id) or env var (BASE_URL / ENCW_ENV / ENCW_INSTANCE_ID / ENCW_USER_ID / ENCW_PASSWORD).`
29
29
  );
30
30
  this.missing = missing;
31
31
  this.name = "MissingRequiredParamError";
@@ -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,17 @@ class RequiredEnvParamsResolver {
34
36
  if (missing.length > 0) throw new import_missingRequiredParamError.MissingRequiredParamError(missing);
35
37
  return params;
36
38
  }
39
+ // Precedence: 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 (process.env.BASE_URL) return process.env.BASE_URL;
44
+ const envName = cliOpts.env || process.env.ENCW_ENV;
45
+ if (!envName) return "";
46
+ const envs = cliOpts.envs ?? {};
47
+ const fromMap = envs[envName];
48
+ if (!fromMap) throw new import_unknownEnvError.UnknownEnvError(envName, Object.keys(envs));
49
+ return fromMap;
39
50
  }
40
51
  resolveInstanceId(cliOpts) {
41
52
  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(
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);
@@ -53,7 +59,8 @@ class RunCommand {
53
59
  ]);
54
60
  const resolved = loader.resolveOptions();
55
61
  const env = deps.envParams.resolve({
56
- baseUrl: options.baseUrl,
62
+ env: options.env,
63
+ envs: resolved.envs,
57
64
  instanceId: options.instanceId,
58
65
  userId: options.userId
59
66
  });
@@ -2,7 +2,7 @@ class MissingRequiredParamError extends Error {
2
2
  constructor(missing) {
3
3
  super(
4
4
  `Missing required parameter(s): ${missing.join(", ")}.
5
- Provide via CLI flag (--base-url / --instance-id / --user-id) or env var (BASE_URL / ENCW_INSTANCE_ID / ENCW_USER_ID / ENCW_PASSWORD).`
5
+ Provide via CLI flag (--env / --instance-id / --user-id) or env var (BASE_URL / ENCW_ENV / ENCW_INSTANCE_ID / ENCW_USER_ID / ENCW_PASSWORD).`
6
6
  );
7
7
  this.missing = missing;
8
8
  this.name = "MissingRequiredParamError";
@@ -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,17 @@ class RequiredEnvParamsResolver {
10
11
  if (missing.length > 0) throw new MissingRequiredParamError(missing);
11
12
  return params;
12
13
  }
14
+ // Precedence: 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 (process.env.BASE_URL) return process.env.BASE_URL;
19
+ const envName = cliOpts.env || process.env.ENCW_ENV;
20
+ if (!envName) return "";
21
+ const envs = cliOpts.envs ?? {};
22
+ const fromMap = envs[envName];
23
+ if (!fromMap) throw new UnknownEnvError(envName, Object.keys(envs));
24
+ return fromMap;
15
25
  }
16
26
  resolveInstanceId(cliOpts) {
17
27
  return cliOpts.instanceId || process.env.ENCW_INSTANCE_ID || "";
@@ -33,5 +43,6 @@ class RequiredEnvParamsResolver {
33
43
  }
34
44
  export {
35
45
  MissingRequiredParamError,
36
- RequiredEnvParamsResolver
46
+ RequiredEnvParamsResolver,
47
+ UnknownEnvError
37
48
  };
@@ -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
  };