@memberjunction/testing-cli 6.1.3 → 6.2.0-edge.0

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.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @fileoverview Replay-script review and promotion
3
+ * @module @memberjunction/testing-cli
4
+ */
5
+ import { UserInfo } from '@memberjunction/core';
6
+ import { ScriptsFlags } from '../types.js';
7
+ /**
8
+ * `mj test scripts` — the review gate for replay scripts.
9
+ *
10
+ * A run that re-derives a test which already had a script writes the new one to
11
+ * `Configuration.PendingReplayScript` rather than replacing what replay uses. This
12
+ * command is where a human sees what changed and decides. Without it, a UI change
13
+ * would silently rewrite the suite's scripts and stay green.
14
+ */
15
+ export declare class ScriptsCommand {
16
+ execute(flags: ScriptsFlags, contextUser?: UserInfo): Promise<void>;
17
+ /** Every test matching a name-or-ID filter, regardless of pending state. */
18
+ private findTests;
19
+ /** Tests carrying a pending script, optionally narrowed to one by name or ID. */
20
+ private collectPending;
21
+ /** Print the drift each pending script represents, newest information first. */
22
+ private report;
23
+ /** Promote or discard, one save per test. */
24
+ private resolve;
25
+ }
26
+ //# sourceMappingURL=scripts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scripts.d.ts","sourceRoot":"","sources":["../../src/commands/scripts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAexC;;;;;;;GAOG;AACH,qBAAa,cAAc;IACjB,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,WAAW,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA4DzE,4EAA4E;IAC5E,OAAO,CAAC,SAAS;IAIjB,iFAAiF;IACjF,OAAO,CAAC,cAAc;IA2BtB,gFAAgF;IAChF,OAAO,CAAC,MAAM;IAgBd,6CAA6C;YAC/B,OAAO;CA6BxB"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * @fileoverview Replay-script review and promotion
3
+ * @module @memberjunction/testing-cli
4
+ */
5
+ import { TestEngine } from '@memberjunction/testing-engine';
6
+ import { UUIDsEqual } from '@memberjunction/global';
7
+ import { OutputFormatter } from '../utils/output-formatter.js';
8
+ import { initializeMJProvider, closeMJProvider, getContextUser } from '../lib/mj-provider.js';
9
+ import { summarizeScriptDrift } from '../utils/script-drift.js';
10
+ import chalk from 'chalk';
11
+ /**
12
+ * `mj test scripts` — the review gate for replay scripts.
13
+ *
14
+ * A run that re-derives a test which already had a script writes the new one to
15
+ * `Configuration.PendingReplayScript` rather than replacing what replay uses. This
16
+ * command is where a human sees what changed and decides. Without it, a UI change
17
+ * would silently rewrite the suite's scripts and stay green.
18
+ */
19
+ export class ScriptsCommand {
20
+ async execute(flags, contextUser) {
21
+ try {
22
+ await initializeMJProvider();
23
+ if (!contextUser) {
24
+ contextUser = await getContextUser();
25
+ }
26
+ const engine = TestEngine.Instance;
27
+ await engine.Config(false, contextUser);
28
+ const matchedTests = flags.test ? this.findTests(engine, flags.test) : undefined;
29
+ if (matchedTests && matchedTests.length === 0) {
30
+ // A name nobody has is not the same as a test with nothing pending.
31
+ // Reporting both as "no pending script" made a typo look like success.
32
+ console.log(chalk.yellow(`\nNo test matches "${flags.test}".\n`));
33
+ process.exitCode = 1;
34
+ await closeMJProvider();
35
+ return;
36
+ }
37
+ const entries = this.collectPending(engine, flags.test);
38
+ if (entries.length === 0) {
39
+ console.log(chalk.gray(flags.test
40
+ ? `\nNo pending replay script for "${flags.test}".\n`
41
+ : '\nNo pending replay scripts — every recorded script matches what replay is using.\n'));
42
+ await closeMJProvider();
43
+ return;
44
+ }
45
+ if ((flags.promote || flags.discard) && !flags.test && !flags.yes && entries.length > 1) {
46
+ // Resolving every pending script at once is the operation you least
47
+ // want to perform by accident: promoting ratifies UI changes nobody
48
+ // looked at, discarding throws away recordings that cost model time.
49
+ this.report(entries);
50
+ console.log(chalk.yellow(`This would ${flags.promote ? 'promote' : 'discard'} all ${entries.length} pending script(s) at once.\n` +
51
+ `Re-run with --yes to confirm, or narrow it with --test "<name>".\n`));
52
+ process.exitCode = 1;
53
+ await closeMJProvider();
54
+ return;
55
+ }
56
+ if (flags.promote || flags.discard) {
57
+ await this.resolve(entries, flags);
58
+ }
59
+ else {
60
+ this.report(entries);
61
+ }
62
+ await closeMJProvider();
63
+ }
64
+ catch (error) {
65
+ console.error(OutputFormatter.formatError('Failed to review replay scripts', error));
66
+ try {
67
+ await closeMJProvider();
68
+ }
69
+ catch {
70
+ // Ignore cleanup errors
71
+ }
72
+ process.exit(1);
73
+ }
74
+ }
75
+ /** Every test matching a name-or-ID filter, regardless of pending state. */
76
+ findTests(engine, testFilter) {
77
+ return engine.Tests.filter(t => t.Name === testFilter || UUIDsEqual(t.ID, testFilter));
78
+ }
79
+ /** Tests carrying a pending script, optionally narrowed to one by name or ID. */
80
+ collectPending(engine, testFilter) {
81
+ const entries = [];
82
+ for (const test of engine.Tests) {
83
+ if (testFilter && test.Name !== testFilter && !UUIDsEqual(test.ID, testFilter)) {
84
+ continue;
85
+ }
86
+ let config;
87
+ try {
88
+ config = test.ConfigurationObject;
89
+ }
90
+ catch {
91
+ continue; // malformed Configuration — not this command's problem to report
92
+ }
93
+ const pending = config?.PendingReplayScript;
94
+ if (!config || !pending) {
95
+ continue;
96
+ }
97
+ entries.push({
98
+ test,
99
+ config,
100
+ pending,
101
+ current: config.ReplayScript,
102
+ drift: summarizeScriptDrift(config.ReplayScript, pending),
103
+ });
104
+ }
105
+ return entries;
106
+ }
107
+ /** Print the drift each pending script represents, newest information first. */
108
+ report(entries) {
109
+ console.log(chalk.bold(`\nPending replay scripts (${entries.length}):\n`));
110
+ for (const e of entries) {
111
+ const heading = e.drift.meaningfulDrift > 0 ? chalk.yellow(e.test.Name) : chalk.cyan(e.test.Name);
112
+ console.log(` ${heading}`);
113
+ console.log(chalk.gray(` ${e.drift.summary}`));
114
+ for (const change of e.drift.changes.slice(0, 8)) {
115
+ console.log(chalk.gray(` step ${change.index + 1}: ${change.kind} — ${change.detail}`));
116
+ }
117
+ if (e.drift.changes.length > 8) {
118
+ console.log(chalk.gray(` … and ${e.drift.changes.length - 8} more`));
119
+ }
120
+ }
121
+ console.log(chalk.gray('\n Promote with --promote (add --test "<name>" to pick one), or drop with --discard.\n'));
122
+ }
123
+ /** Promote or discard, one save per test. */
124
+ async resolve(entries, flags) {
125
+ const promoting = !!flags.promote;
126
+ const verb = promoting ? 'Promoted' : 'Discarded';
127
+ let failures = 0;
128
+ for (const e of entries) {
129
+ const next = { ...e.config };
130
+ delete next.PendingReplayScript;
131
+ if (promoting) {
132
+ next.ReplayScript = e.pending;
133
+ }
134
+ e.test.ConfigurationObject = next;
135
+ if (await e.test.Save()) {
136
+ console.log(chalk.green(` ${verb}: ${e.test.Name}`));
137
+ }
138
+ else {
139
+ failures++;
140
+ console.log(chalk.red(` Failed: ${e.test.Name} — ${e.test.LatestResult?.CompleteMessage ?? 'Save() returned false'}`));
141
+ }
142
+ }
143
+ console.log(failures === 0
144
+ ? chalk.bold(`\n${verb} ${entries.length} script(s).\n`)
145
+ : chalk.bold.red(`\n${verb} ${entries.length - failures} of ${entries.length}; ${failures} failed.\n`));
146
+ if (failures > 0) {
147
+ process.exitCode = 1;
148
+ }
149
+ }
150
+ }
151
+ //# sourceMappingURL=scripts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/commands/scripts.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export { ValidateCommand } from './commands/validate.js';
11
11
  export { ReportCommand } from './commands/report.js';
12
12
  export { HistoryCommand } from './commands/history.js';
13
13
  export { CompareCommand } from './commands/compare.js';
14
+ export { ScriptsCommand } from './commands/scripts.js';
14
15
  export { OutputFormatter } from './utils/output-formatter.js';
15
16
  export { loadCLIConfig } from './utils/config-loader.js';
16
17
  export { SpinnerManager } from './utils/spinner-manager.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,cAAc,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ export { ValidateCommand } from './commands/validate.js';
12
12
  export { ReportCommand } from './commands/report.js';
13
13
  export { HistoryCommand } from './commands/history.js';
14
14
  export { CompareCommand } from './commands/compare.js';
15
+ export { ScriptsCommand } from './commands/scripts.js';
15
16
  // Utilities
16
17
  export { OutputFormatter } from './utils/output-formatter.js';
17
18
  export { loadCLIConfig } from './utils/config-loader.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,0BAA0B;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,YAAY;AACZ,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,QAAQ;AACR,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,0BAA0B;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,YAAY;AACZ,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,QAAQ;AACR,cAAc,SAAS,CAAC"}
package/dist/types.d.ts CHANGED
@@ -92,6 +92,17 @@ export interface SuiteFlags extends CommonFlags {
92
92
  /**
93
93
  * Flags for list command
94
94
  */
95
+ /** Flags for `mj test scripts` — replay-script review and promotion. */
96
+ export interface ScriptsFlags extends CommonFlags {
97
+ /** Narrow to one test by name or ID. */
98
+ test?: string;
99
+ /** Promote the pending script(s) into the slot replay uses. */
100
+ promote?: boolean;
101
+ /** Drop the pending script(s), leaving the promoted one in place. */
102
+ discard?: boolean;
103
+ /** Confirm a bulk --promote/--discard that was not narrowed to one test. */
104
+ yes?: boolean;
105
+ }
95
106
  export interface ListFlags extends CommonFlags {
96
107
  type?: string;
97
108
  suites?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @fileoverview Structural diff between a promoted replay script and a pending one
3
+ * @module @memberjunction/testing-cli
4
+ */
5
+ import { MJTestEntity_IReplayScript } from '@memberjunction/core-entities';
6
+ /**
7
+ * How one step's fresh derivation differs from the promoted script.
8
+ *
9
+ * The distinction that matters is `selector-drift` versus the rest: a selector
10
+ * that changed while role and name held is routine churn the healer absorbs,
11
+ * whereas a changed target, verb, or URL means the UI itself moved.
12
+ */
13
+ export type ScriptStepDriftKind = 'selector-drift' | 'target-changed' | 'method-changed' | 'url-changed' | 'input-changed' | 'guard-changed';
14
+ export interface ScriptStepDrift {
15
+ /** 0-based index into the promoted script's steps. */
16
+ index: number;
17
+ kind: ScriptStepDriftKind;
18
+ detail: string;
19
+ }
20
+ export interface ScriptDrift {
21
+ addedSteps: number;
22
+ removedSteps: number;
23
+ changes: ScriptStepDrift[];
24
+ /** Changes excluding routine selector churn — the "the UI moved" count. */
25
+ meaningfulDrift: number;
26
+ /** One line for the review listing. */
27
+ summary: string;
28
+ }
29
+ /**
30
+ * Compare a pending script against the promoted one it would replace.
31
+ *
32
+ * Deliberately a dependency-light structural summary rather than the engine's own
33
+ * `diffTraces`: this package would otherwise need `@memberjunction/computer-use`,
34
+ * which carries a Playwright peer dependency the test CLI has no other use for.
35
+ * Both read the same fields, and this one only has to be good enough for a human
36
+ * to decide with.
37
+ */
38
+ export declare function summarizeScriptDrift(promoted: MJTestEntity_IReplayScript | undefined, pending: MJTestEntity_IReplayScript): ScriptDrift;
39
+ //# sourceMappingURL=script-drift.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-drift.d.ts","sourceRoot":"","sources":["../../src/utils/script-drift.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,0BAA0B,EAAkC,MAAM,+BAA+B,CAAC;AAE3G;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GACzB,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,aAAa,GACb,eAAe,GACf,eAAe,CAAC;AAEtB,MAAM,WAAW,eAAe;IAC5B,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,mBAAmB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,2EAA2E;IAC3E,eAAe,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAChC,QAAQ,EAAE,0BAA0B,GAAG,SAAS,EAChD,OAAO,EAAE,0BAA0B,GACpC,WAAW,CA4Bb"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * @fileoverview Structural diff between a promoted replay script and a pending one
3
+ * @module @memberjunction/testing-cli
4
+ */
5
+ /**
6
+ * Compare a pending script against the promoted one it would replace.
7
+ *
8
+ * Deliberately a dependency-light structural summary rather than the engine's own
9
+ * `diffTraces`: this package would otherwise need `@memberjunction/computer-use`,
10
+ * which carries a Playwright peer dependency the test CLI has no other use for.
11
+ * Both read the same fields, and this one only has to be good enough for a human
12
+ * to decide with.
13
+ */
14
+ export function summarizeScriptDrift(promoted, pending) {
15
+ if (!promoted) {
16
+ return {
17
+ addedSteps: pending.Steps?.length ?? 0,
18
+ removedSteps: 0,
19
+ changes: [],
20
+ meaningfulDrift: 0,
21
+ summary: `new script — ${pending.Steps?.length ?? 0} step(s), no promoted script to compare against`,
22
+ };
23
+ }
24
+ const before = promoted.Steps ?? [];
25
+ const after = pending.Steps ?? [];
26
+ const changes = [];
27
+ for (let i = 0; i < Math.min(before.length, after.length); i++) {
28
+ const change = compareStep(i, before[i], after[i]);
29
+ if (change) {
30
+ changes.push(change);
31
+ }
32
+ }
33
+ const addedSteps = Math.max(0, after.length - before.length);
34
+ const removedSteps = Math.max(0, before.length - after.length);
35
+ const meaningfulDrift = changes.filter(c => c.kind !== 'selector-drift').length + addedSteps + removedSteps;
36
+ return { addedSteps, removedSteps, changes, meaningfulDrift, summary: buildSummary(changes, addedSteps, removedSteps, meaningfulDrift) };
37
+ }
38
+ /** The first difference that matters, or null when the two steps agree. */
39
+ function compareStep(index, before, after) {
40
+ if (before.Action?.Method !== after.Action?.Method) {
41
+ return { index, kind: 'method-changed', detail: `${before.Action?.Method} → ${after.Action?.Method}` };
42
+ }
43
+ if (normalizeUrl(before.Action?.Url) !== normalizeUrl(after.Action?.Url)) {
44
+ return { index, kind: 'url-changed', detail: `${before.Action?.Url ?? '(none)'} → ${after.Action?.Url ?? '(none)'}` };
45
+ }
46
+ // The engine's diffTraces labels a changed entry URL `url-changed` too. Using
47
+ // the same word for only Action.Url meant a step that starts somewhere else
48
+ // entirely was reported as identical.
49
+ if (normalizeUrl(before.UrlBefore) !== normalizeUrl(after.UrlBefore)) {
50
+ return { index, kind: 'url-changed', detail: `entry ${before.UrlBefore ?? '(none)'} → ${after.UrlBefore ?? '(none)'}` };
51
+ }
52
+ const b = before.Action?.Target;
53
+ const a = after.Action?.Target;
54
+ if ((b?.Role ?? '') !== (a?.Role ?? '') || (b?.Name ?? '') !== (a?.Name ?? '')) {
55
+ return {
56
+ index,
57
+ kind: 'target-changed',
58
+ detail: `${describeTarget(b?.Role, b?.Name)} → ${describeTarget(a?.Role, a?.Name)}`,
59
+ };
60
+ }
61
+ // Scope is part of the target's identity — it is what tells same-named twins
62
+ // apart — so a changed region is the UI moving, not selector churn.
63
+ if ((b?.Scope ?? '') !== (a?.Scope ?? '')) {
64
+ return { index, kind: 'target-changed', detail: `scope ${b?.Scope ?? '(none)'} → ${a?.Scope ?? '(none)'}` };
65
+ }
66
+ // What the step actually enters. A recording that types a different value, or
67
+ // stops pressing Enter, is a behavioural change however stable its selector.
68
+ const inputDrift = compareInput(before, after);
69
+ if (inputDrift) {
70
+ return { index, ...inputDrift };
71
+ }
72
+ // What the step asserts. A dropped or rewritten guard is how a script quietly
73
+ // stops checking the thing it was recorded to check.
74
+ const guardDrift = compareGuards(before, after);
75
+ if (guardDrift) {
76
+ return { index, ...guardDrift };
77
+ }
78
+ if ((b?.Selector ?? '') !== (a?.Selector ?? '')) {
79
+ return { index, kind: 'selector-drift', detail: `${b?.Selector ?? '(none)'} → ${a?.Selector ?? '(none)'}` };
80
+ }
81
+ return null;
82
+ }
83
+ /** Text / Key / PressEnter — the payload the step delivers. */
84
+ function compareInput(before, after) {
85
+ const fields = ['Text', 'Key'];
86
+ for (const field of fields) {
87
+ const b = before.Action?.[field] ?? '';
88
+ const a = after.Action?.[field] ?? '';
89
+ if (b !== a) {
90
+ return { kind: 'input-changed', detail: `${field} "${b}" → "${a}"` };
91
+ }
92
+ }
93
+ if ((before.Action?.PressEnter ?? false) !== (after.Action?.PressEnter ?? false)) {
94
+ return { kind: 'input-changed', detail: `PressEnter ${before.Action?.PressEnter ?? false} → ${after.Action?.PressEnter ?? false}` };
95
+ }
96
+ return null;
97
+ }
98
+ /** Precondition / Postcondition — what the step waits for and what it proves. */
99
+ function compareGuards(before, after) {
100
+ const pre = describeGuard(before.Precondition);
101
+ const preAfter = describeGuard(after.Precondition);
102
+ if (pre !== preAfter) {
103
+ return { kind: 'guard-changed', detail: `precondition ${pre} → ${preAfter}` };
104
+ }
105
+ const post = describeGuard(before.Postcondition);
106
+ const postAfter = describeGuard(after.Postcondition);
107
+ if (post !== postAfter) {
108
+ return { kind: 'guard-changed', detail: `postcondition ${post} → ${postAfter}` };
109
+ }
110
+ return null;
111
+ }
112
+ /** Stable text for a guard, with URL patterns normalized so token churn is not drift. */
113
+ function describeGuard(guard) {
114
+ if (!guard) {
115
+ return '(none)';
116
+ }
117
+ const g = guard;
118
+ const parts = Object.keys(g)
119
+ .sort()
120
+ .filter(k => g[k] !== undefined)
121
+ .map(k => `${k}=${k.endsWith('UrlPattern') ? normalizeUrl(String(g[k])) : String(g[k])}`);
122
+ return parts.length > 0 ? parts.join(',') : '(none)';
123
+ }
124
+ function describeTarget(role, name) {
125
+ if (!role && !name) {
126
+ return '(no target)';
127
+ }
128
+ return `${role ?? '?'} "${name ?? ''}"`;
129
+ }
130
+ /** Recording already tokenizes UUIDs, so a raw compare is enough here. */
131
+ function normalizeUrl(url) {
132
+ return (url ?? '').trim();
133
+ }
134
+ function buildSummary(changes, added, removed, meaningful) {
135
+ if (changes.length === 0 && added === 0 && removed === 0) {
136
+ return 'identical to the promoted script';
137
+ }
138
+ const parts = [];
139
+ if (added > 0) {
140
+ parts.push(`${added} step(s) added`);
141
+ }
142
+ if (removed > 0) {
143
+ parts.push(`${removed} step(s) removed`);
144
+ }
145
+ const selectorOnly = changes.filter(c => c.kind === 'selector-drift').length;
146
+ if (selectorOnly > 0) {
147
+ parts.push(`${selectorOnly} selector-only change(s)`);
148
+ }
149
+ const structural = changes.length - selectorOnly;
150
+ if (structural > 0) {
151
+ parts.push(`${structural} target/verb/URL change(s)`);
152
+ }
153
+ return `${parts.join(', ')} — ${meaningful === 0 ? 'routine churn' : `${meaningful} meaningful`}`;
154
+ }
155
+ //# sourceMappingURL=script-drift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-drift.js","sourceRoot":"","sources":["../../src/utils/script-drift.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memberjunction/testing-cli",
3
3
  "type": "module",
4
- "version": "6.1.3",
4
+ "version": "6.2.0-edge.0",
5
5
  "description": "MemberJunction Testing Framework CLI - Command-line interface for test execution and management",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -17,15 +17,15 @@
17
17
  "@types/mssql": "^9.1.9"
18
18
  },
19
19
  "dependencies": {
20
- "@memberjunction/global": "6.1.3",
21
- "@memberjunction/core": "6.1.3",
22
- "@memberjunction/dynamic-packages": "6.1.3",
23
- "@memberjunction/core-entities": "6.1.3",
24
- "@memberjunction/testing-engine": "6.1.3",
25
- "@memberjunction/testing-engine-base": "6.1.3",
26
- "@memberjunction/testing-integration": "6.1.3",
27
- "@memberjunction/generic-database-provider": "6.1.3",
28
- "@memberjunction/sqlserver-dataprovider": "6.1.3",
20
+ "@memberjunction/global": "6.2.0-edge.0",
21
+ "@memberjunction/core": "6.2.0-edge.0",
22
+ "@memberjunction/dynamic-packages": "6.2.0-edge.0",
23
+ "@memberjunction/core-entities": "6.2.0-edge.0",
24
+ "@memberjunction/testing-engine": "6.2.0-edge.0",
25
+ "@memberjunction/testing-engine-base": "6.2.0-edge.0",
26
+ "@memberjunction/testing-integration": "6.2.0-edge.0",
27
+ "@memberjunction/generic-database-provider": "6.2.0-edge.0",
28
+ "@memberjunction/sqlserver-dataprovider": "6.2.0-edge.0",
29
29
  "@oclif/core": "^3.27.0",
30
30
  "ora-classic": "^5.4.2",
31
31
  "chalk": "^5.6.2",
@@ -40,7 +40,7 @@
40
40
  "url": "https://github.com/MemberJunction/MJ"
41
41
  },
42
42
  "optionalDependencies": {
43
- "@memberjunction/postgresql-dataprovider": "6.1.3"
43
+ "@memberjunction/postgresql-dataprovider": "6.2.0-edge.0"
44
44
  },
45
45
  "scripts": {
46
46
  "start": "ts-node-dev src/index.ts",