@simplysf/simply-utam 0.2.0 → 0.3.1

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 (34) hide show
  1. package/README.md +124 -1
  2. package/lib/clipboard.d.ts +31 -0
  3. package/lib/clipboard.js +98 -0
  4. package/lib/clipboard.js.map +1 -0
  5. package/lib/commands/init.d.ts +2 -1
  6. package/lib/commands/init.js +26 -12
  7. package/lib/commands/init.js.map +1 -1
  8. package/lib/commands/snapshot/console/import.d.ts +26 -0
  9. package/lib/commands/snapshot/console/import.js +96 -0
  10. package/lib/commands/snapshot/console/import.js.map +1 -0
  11. package/lib/commands/snapshot/console/snippet.d.ts +25 -0
  12. package/lib/commands/snapshot/console/snippet.js +72 -0
  13. package/lib/commands/snapshot/console/snippet.js.map +1 -0
  14. package/lib/commands/snapshot/find.d.ts +25 -0
  15. package/lib/commands/snapshot/find.js +82 -0
  16. package/lib/commands/snapshot/find.js.map +1 -0
  17. package/lib/commands/snapshot/lookup.d.ts +20 -0
  18. package/lib/commands/snapshot/lookup.js +57 -0
  19. package/lib/commands/snapshot/lookup.js.map +1 -0
  20. package/lib/commands/snapshot/node.d.ts +22 -0
  21. package/lib/commands/snapshot/node.js +61 -0
  22. package/lib/commands/snapshot/node.js.map +1 -0
  23. package/lib/commands/snapshot/path.d.ts +21 -0
  24. package/lib/commands/snapshot/path.js +77 -0
  25. package/lib/commands/snapshot/path.js.map +1 -0
  26. package/lib/commands/snapshot/tree.d.ts +19 -0
  27. package/lib/commands/snapshot/tree.js +50 -0
  28. package/lib/commands/snapshot/tree.js.map +1 -0
  29. package/lib/snapshot-flags.d.ts +40 -0
  30. package/lib/snapshot-flags.js +70 -0
  31. package/lib/snapshot-flags.js.map +1 -0
  32. package/oclif.manifest.json +552 -2
  33. package/package.json +16 -4
  34. package/skills/utam-dom-snapshots/SKILL.md +390 -0
package/README.md CHANGED
@@ -16,12 +16,135 @@ Requires Node.js 22 or later.
16
16
 
17
17
  ## Commands
18
18
 
19
- - `simply-utam init`: Scaffolds starter UTAM configs (`generator.config.json`, `utam.config.json`, `wdio.conf.mjs`, `.utam/namespace-map.json`), non-destructively injects standard Wireit tasks into `package.json`, and adds any missing UTAM rules to `.forceignore` under a `# Ignore UTAM` comment so LWC bundles that contain UTAM files deploy successfully. Existing lines are never changed; new rules go inside the `# -- START CUSTOMIZATION` / `# -- END CUSTOMIZATION` region when one exists (`--forceignore-region-start` / `--forceignore-region-end` to change the markers, `--no-forceignore` to skip). The UTAM namespace is the `package.json` name, scope included, so page objects can import each other by the package's own name (`--app-name` to override). It also adds `"./pageObjects/*": "./pageObjects/*.js"` to `package.json` `exports`, which those imports resolve through; an existing entry is never changed.
19
+ - `simply-utam init`: Scaffolds starter UTAM configs (`generator.config.json`, `utam.config.json`, `wdio.conf.mjs`, `.utam/namespace-map.json`) and the DOM snapshot capture step (`step_definitions/dom-snapshot.steps.mjs`) in the UTAM test folder, adds `.utam/snapshots/` to `.gitignore` (`--no-gitignore` to skip), non-destructively injects standard Wireit tasks into `package.json`, and adds any missing UTAM rules to `.forceignore` under a `# Ignore UTAM` comment so LWC bundles that contain UTAM files deploy successfully. Existing lines are never changed; new rules go inside the `# -- START CUSTOMIZATION` / `# -- END CUSTOMIZATION` region when one exists (`--forceignore-region-start` / `--forceignore-region-end` to change the markers, `--no-forceignore` to skip). The UTAM namespace is the `package.json` name, scope included, so page objects can import each other by the package's own name (`--app-name` to override). It also adds `"./pageObjects/*": "./pageObjects/*.js"` to `package.json` `exports`, which those imports resolve through; an existing entry is never changed.
20
20
  - `simply-utam rules`: Scans LWC component definitions and generates or updates UTAM `.rules.json` files for root components.
21
21
  - `simply-utam overrides`: Deeply merges custom element assertions, wait conditions, and visibility rules from `*.utam-overrides.json` into generated `*.utam.json` files while preserving file indentation.
22
22
  - `simply-utam rewrite`: Rewrites namespace prefixes across compiled `*.utam.json` files according to `.utam/namespace-map.json`.
23
23
  - `simply-utam steps`: Statically analyzes Gherkin `.feature` files, compiles existing step expressions, detects true undefined steps, and appends formatted Cucumber expressions into target step files.
24
24
  - `simply-utam build`: Convenience runner executing all compilation and transformation stages sequentially (`rules -> utam-generate -> overrides -> rewrite -> utam`).
25
+ - `simply-utam snapshot find`: Finds nodes in a DOM snapshot by `--text`, `--attr`, `--tag` or `--class`, visible matches first; or tests a CSS selector with `--css` as UTAM's `findElement` runs it, in the document or `--within` a node's light DOM or, with `--shadow`, its shadow root. `--all` lists hidden matches in full.
26
+ - `simply-utam snapshot path`: Shows the custom elements from the document down to a node (`--node` or `--text`), with each shadow crossing, the page object for each element, the route to it from the page object above, and warnings for a root selector that loads a hidden copy or an element declared in the wrong scope. Exits 1 when `--text` doesn't find exactly one visible node.
27
+ - `simply-utam snapshot tree`: Prints the tree under a node (`--node`, `--depth`), shadow root first, then light DOM.
28
+ - `simply-utam snapshot node`: Shows one node in full, or with `--ancestors`, its ancestors nearest first.
29
+ - Every `snapshot` command but `lookup` reads `.utam/snapshots/latest.json` (or `--file`) and prints a header naming it, with a failed step's error and, for UTAM's not-found error, the `find --css` that repeats its search. All take `--json`, `--limit` and `--project-dir`, and exit 2 on bad input.
30
+ - `simply-utam snapshot console snippet`: Prints the snippet to paste into a browser's DevTools console to capture a DOM snapshot of any page, with no test run, or copies it with `--copy`. `--mask` (`structure`, `labels`, `aria` or `full`) fixes the mask mode in the snippet. The snippet copies the masked snapshot to the clipboard, or downloads it where the console's `copy` isn't available.
31
+ - `simply-utam snapshot console import`: Saves the snapshot the snippet copied (or `--file <path>`, `--file -` for standard input) to `.utam/snapshots/manual/` and `latest.json`, with an optional `--label`, and prints its header. Exits 2 on bad input.
32
+ - `simply-utam snapshot lookup`: Shows the page objects for a custom element tag or a page object name, from the project's own page objects and the page-object packages its namespaces map to (`salesforce-pageobjects` through the scaffolded `lightning` namespace): import path, methods and what they do, and public and private elements with their light or shadow scope. `--search <words>` finds page objects by name, tag or description. Needs no snapshot. Takes `--json`, `--limit` and `--project-dir`. See [design 0003](../../docs/design/0003-dom-snapshots.md).
33
+
34
+ ## DOM snapshots in an existing project
35
+
36
+ `init` writes `wdio.conf.mjs` only when it doesn't exist yet, so an existing project's configuration
37
+ doesn't capture DOM snapshots until you add the hooks. Merge these into your `config`, keeping any
38
+ hooks you already have (`afterStep` most likely also takes a screenshot):
39
+
40
+ ```js
41
+ import allureReporter from '@wdio/allure-reporter';
42
+
43
+ // Imported here rather than with a static import, so a project whose @simplysf/simply-utam predates
44
+ // DOM snapshots still loads this config: the snapshot hooks then skip, and onPrepare says to upgrade.
45
+ const snapshots = await loadSnapshotApi();
46
+
47
+ async function loadSnapshotApi() {
48
+ const api = await import('@simplysf/simply-utam');
49
+ const names = [
50
+ 'captureDomSnapshot',
51
+ 'pruneDomSnapshots',
52
+ 'serializeDomSnapshot',
53
+ 'snapshotRunId',
54
+ 'writeDomSnapshot',
55
+ ];
56
+ return names.every((name) => typeof api[name] === 'function') ? api : undefined;
57
+ }
58
+
59
+ export const config = {
60
+ // …
61
+ onPrepare: async function () {
62
+ if (!snapshots) {
63
+ console.warn(
64
+ 'The installed @simplysf/simply-utam has no DOM snapshots, so none are captured. Upgrade it in package.json to capture them.',
65
+ );
66
+ return;
67
+ }
68
+ // One DOM snapshot folder per test run, shared by every worker.
69
+ process.env.UTAM_SNAPSHOT_RUN_ID ??= snapshots.snapshotRunId();
70
+ },
71
+ onComplete: async function () {
72
+ if (!snapshots) return;
73
+ // Keep the last 10 runs, this one included. Housekeeping must never cost the run: on Windows a
74
+ // locked old run folder can't be deleted.
75
+ try {
76
+ await snapshots.pruneDomSnapshots({ keepRuns: 10 });
77
+ } catch (pruneError) {
78
+ console.warn(`Old DOM snapshot runs not pruned: ${pruneError?.message ?? pruneError}`);
79
+ }
80
+ },
81
+ before: async function () {
82
+ if (!snapshots) return;
83
+ // browser.domSnapshot('label') captures the page from the REPL during browser.debug().
84
+ browser.addCommand('domSnapshot', async (label) => {
85
+ const snapshot = await snapshots.captureDomSnapshot({ trigger: 'manual', label });
86
+ return snapshot ? snapshots.writeDomSnapshot(snapshot) : undefined;
87
+ });
88
+ },
89
+ afterStep: async function (step, scenario, { error }) {
90
+ if (error) {
91
+ // Capture the DOM snapshot first, before anything else changes the page.
92
+ const snapshot = await snapshots?.captureDomSnapshot({
93
+ trigger: 'failure',
94
+ scenario: scenario.name,
95
+ step: step.text,
96
+ error,
97
+ });
98
+ if (snapshot) {
99
+ // Saving and attaching each get their own try: neither failing may cost the other, or the
100
+ // screenshot below.
101
+ try {
102
+ console.log(`DOM snapshot written to ${await snapshots.writeDomSnapshot(snapshot)}`);
103
+ } catch (writeError) {
104
+ console.warn(`DOM snapshot not saved: ${writeError?.message ?? writeError}`);
105
+ }
106
+ try {
107
+ allureReporter.addAttachment(
108
+ 'dom-snapshot.json',
109
+ snapshots.serializeDomSnapshot(snapshot),
110
+ 'application/json',
111
+ );
112
+ } catch (attachError) {
113
+ console.warn(`DOM snapshot not attached to the Allure report: ${attachError?.message ?? attachError}`);
114
+ }
115
+ }
116
+ await browser.takeScreenshot();
117
+ }
118
+ },
119
+ };
120
+ ```
121
+
122
+ Also set `cucumberOpts.timeout` above `waitforTimeout` (the scaffold adds 30 seconds), so a step that
123
+ waits for a missing element fails with UTAM's error, which names the locator, rather than only
124
+ Cucumber's timeout. `init` adds the capture step to an existing project's UTAM test folder, since
125
+ it's a new file. For the guide, install the [`utam-dom-snapshots` skill](#agent-skills).
126
+
127
+ ## Agent skills
128
+
129
+ The package ships agent skills for working with simply-utam in your project: guides that a coding
130
+ agent (or a person) follows to use its commands. Each is a folder under `skills/`, with a `SKILL.md`
131
+ whose frontmatter tells an agent when to load it. None is installed for you; add the ones you want.
132
+
133
+ | Skill | Use it when |
134
+ | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
135
+ | [`utam-dom-snapshots`](skills/utam-dom-snapshots/SKILL.md) | Writing or fixing a UTAM step definition, page object or selector: capturing DOM snapshots, querying them, and looking up page objects. |
136
+
137
+ Link a skill into your agent's skills folder, so it stays in step with the installed version of
138
+ simply-utam. For Claude Code, from the project folder:
139
+
140
+ ```bash
141
+ mkdir -p .claude/skills
142
+ ln -s ../../node_modules/@simplysf/simply-utam/skills/utam-dom-snapshots .claude/skills/utam-dom-snapshots
143
+ ```
144
+
145
+ Or copy the folder instead, to edit it; a copy doesn't change when you upgrade simply-utam, so
146
+ copy it again after an upgrade. Other agents read skills from their own folders; link or copy the
147
+ skill there the same way.
25
148
 
26
149
  ## Usage & Wireit Integration
27
150
 
@@ -0,0 +1,31 @@
1
+ /** What a command printed, and how it exited. */
2
+ export type CommandResult = {
3
+ code: number | null;
4
+ stdout: string;
5
+ };
6
+ /** Runs a command with `input` on its standard input. Rejects when the command can't start. */
7
+ export type CommandRunner = (command: string, args: string[], input?: string) => Promise<CommandResult>;
8
+ /** The system clipboard, as text. */
9
+ export type Clipboard = {
10
+ read(): Promise<string>;
11
+ write(text: string): Promise<void>;
12
+ };
13
+ /**
14
+ * Runs a command, collecting its standard output when it's given no input. A command given input is
15
+ * writing, and its output is ignored: `xclip` and `wl-copy` leave a process behind to serve the
16
+ * clipboard, and one holding the output pipe would keep `close` from firing until the clipboard changes.
17
+ */
18
+ export declare const runCommand: CommandRunner;
19
+ /** Thrown when no clipboard tool for this platform works. */
20
+ export declare class ClipboardUnavailableError extends Error {
21
+ }
22
+ /**
23
+ * The system clipboard, through the platform's own tools: `pbcopy` and `pbpaste` on macOS, PowerShell
24
+ * on Windows, and `wl-copy`, `xclip` or `xsel` on Linux.
25
+ *
26
+ * @param options The platform (the current one by default) and how commands are run, for tests.
27
+ */
28
+ export declare function systemClipboard(options?: {
29
+ platform?: NodeJS.Platform;
30
+ run?: CommandRunner;
31
+ }): Clipboard;
@@ -0,0 +1,98 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { spawn } from 'node:child_process';
17
+ // Windows PowerShell writes the console's code page unless told otherwise.
18
+ const POWERSHELL = ['-NoProfile', '-NonInteractive', '-Command'];
19
+ const UTF8 = '[Console]::OutputEncoding = [Text.Encoding]::UTF8; [Console]::InputEncoding = [Text.Encoding]::UTF8; ';
20
+ const READ_TOOLS = {
21
+ darwin: [{ command: 'pbpaste', args: [] }],
22
+ win32: [{ command: 'powershell', args: [...POWERSHELL, `${UTF8}Get-Clipboard -Raw`] }],
23
+ linux: [
24
+ { command: 'wl-paste', args: ['--no-newline'] },
25
+ { command: 'xclip', args: ['-selection', 'clipboard', '-o'] },
26
+ { command: 'xsel', args: ['--clipboard', '--output'] },
27
+ ],
28
+ };
29
+ const WRITE_TOOLS = {
30
+ darwin: [{ command: 'pbcopy', args: [] }],
31
+ win32: [{ command: 'powershell', args: [...POWERSHELL, `${UTF8}Set-Clipboard -Value ([Console]::In.ReadToEnd())`] }],
32
+ linux: [
33
+ { command: 'wl-copy', args: [] },
34
+ { command: 'xclip', args: ['-selection', 'clipboard'] },
35
+ { command: 'xsel', args: ['--clipboard', '--input'] },
36
+ ],
37
+ };
38
+ /**
39
+ * Runs a command, collecting its standard output when it's given no input. A command given input is
40
+ * writing, and its output is ignored: `xclip` and `wl-copy` leave a process behind to serve the
41
+ * clipboard, and one holding the output pipe would keep `close` from firing until the clipboard changes.
42
+ */
43
+ export const runCommand = (command, args, input) => new Promise((resolve, reject) => {
44
+ const reading = input === undefined;
45
+ const child = spawn(command, args, { stdio: ['pipe', reading ? 'pipe' : 'ignore', 'ignore'], windowsHide: true });
46
+ const chunks = [];
47
+ child.stdout?.on('data', (chunk) => chunks.push(chunk));
48
+ child.on('error', reject);
49
+ child.on('close', (code) => resolve({ code, stdout: Buffer.concat(chunks).toString('utf8') }));
50
+ // A tool that exits without reading its input closes the pipe; `close` still reports how it exited.
51
+ child.stdin?.on('error', () => undefined);
52
+ child.stdin?.end(input ?? '');
53
+ });
54
+ /** Thrown when no clipboard tool for this platform works. */
55
+ export class ClipboardUnavailableError extends Error {
56
+ }
57
+ async function firstWorking(tools, run, input) {
58
+ // Tools that ran but exited with an error, rather than not being installed.
59
+ const failed = [];
60
+ for (const tool of tools) {
61
+ try {
62
+ // Tools are tried one at a time: the first that works is the one to use.
63
+ // eslint-disable-next-line no-await-in-loop
64
+ const result = await run(tool.command, tool.args, input);
65
+ if (result.code === 0)
66
+ return result.stdout;
67
+ failed.push(tool.command);
68
+ }
69
+ catch {
70
+ // Not installed; try the next one.
71
+ }
72
+ }
73
+ if (tools.length === 0)
74
+ throw new ClipboardUnavailableError('no clipboard tool for this platform');
75
+ const tried = tools.map((tool) => tool.command).join(', ');
76
+ // Paste tools such as wl-paste and xclip exit with an error when the clipboard holds no text.
77
+ const ranButFailed = failed.length === 0
78
+ ? ''
79
+ : `; ${failed.join(', ')} ran but failed${input === undefined ? ', as it does when the clipboard is empty' : ''}`;
80
+ throw new ClipboardUnavailableError(`no clipboard tool worked (tried ${tried})${ranButFailed}`);
81
+ }
82
+ /**
83
+ * The system clipboard, through the platform's own tools: `pbcopy` and `pbpaste` on macOS, PowerShell
84
+ * on Windows, and `wl-copy`, `xclip` or `xsel` on Linux.
85
+ *
86
+ * @param options The platform (the current one by default) and how commands are run, for tests.
87
+ */
88
+ export function systemClipboard(options = {}) {
89
+ const platform = options.platform ?? process.platform;
90
+ const run = options.run ?? runCommand;
91
+ return {
92
+ read: () => firstWorking(READ_TOOLS[platform] ?? [], run),
93
+ async write(text) {
94
+ await firstWorking(WRITE_TOOLS[platform] ?? [], run, text);
95
+ },
96
+ };
97
+ }
98
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../src/clipboard.ts"],"names":[],"mappings":"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"}
@@ -17,8 +17,9 @@ export default class Init extends Command {
17
17
  forceignore: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
18
  'forceignore-region-start': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
19
19
  'forceignore-region-end': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
20
+ gitignore: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
21
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
21
22
  };
22
23
  run(): Promise<ScaffoldResult>;
23
- private logForceignore;
24
+ private logIgnoreFile;
24
25
  }
@@ -16,12 +16,13 @@
16
16
  import * as path from 'node:path';
17
17
  import { Command, Flags } from '@oclif/core';
18
18
  import { DEFAULT_FORCEIGNORE_REGION, scaffoldProject, } from '@simplysf/simply-utam-core';
19
+ const SNAPSHOT_HOOKS_DOCS = 'https://github.com/SimplySF/simply-utam/tree/main/packages/simply-utam#dom-snapshots-in-an-existing-project';
19
20
  /**
20
21
  * CLI command to scaffold SimplyUTAM configurations, Wireit tasks, and namespace maps.
21
22
  */
22
23
  export default class Init extends Command {
23
24
  static summary = 'Scaffold UTAM configurations, Wireit tasks, and namespace maps in a Salesforce project.';
24
- static description = 'Inspects the target project, generates generator.config.json, utam.config.json, wdio.conf.mjs, and .utam/namespace-map.json, non-destructively injects standard Wireit tasks into package.json, and adds any missing UTAM rules to .forceignore under a "# Ignore UTAM" comment without changing existing lines.';
25
+ static description = 'Inspects the target project, generates generator.config.json, utam.config.json, wdio.conf.mjs, and .utam/namespace-map.json, adds the DOM snapshot capture step to the UTAM test folder, non-destructively injects standard Wireit tasks into package.json, and adds any missing UTAM rules to .forceignore and .gitignore under a "# Ignore UTAM" comment without changing existing lines.';
25
26
  static examples = [
26
27
  '<%= config.bin %> <%= command.id %>',
27
28
  '<%= config.bin %> <%= command.id %> --source-dir force-app --app-name my-app',
@@ -65,6 +66,11 @@ export default class Init extends Command {
65
66
  summary: 'Exact line that ends the .forceignore customization region.',
66
67
  default: DEFAULT_FORCEIGNORE_REGION.end,
67
68
  }),
69
+ gitignore: Flags.boolean({
70
+ summary: 'Add .utam/snapshots/ to .gitignore, so DOM snapshots stay out of git.',
71
+ default: true,
72
+ allowNo: true,
73
+ }),
68
74
  verbose: Flags.boolean({
69
75
  char: 'v',
70
76
  summary: 'Enable detailed output.',
@@ -83,6 +89,7 @@ export default class Init extends Command {
83
89
  forceignore: flags.forceignore,
84
90
  forceignoreRegionStart: flags['forceignore-region-start'],
85
91
  forceignoreRegionEnd: flags['forceignore-region-end'],
92
+ gitignore: flags.gitignore,
86
93
  });
87
94
  if (flags['dry-run']) {
88
95
  this.log(`[dry-run] Project scaffolding preview for: ${resolvedTarget}`);
@@ -109,7 +116,14 @@ export default class Init extends Command {
109
116
  }
110
117
  }
111
118
  if (result.forceignore) {
112
- this.logForceignore(result.forceignore, flags['dry-run']);
119
+ this.logIgnoreFile(result.forceignore, flags['dry-run'], 'UTAM artifacts');
120
+ }
121
+ if (result.gitignore) {
122
+ this.logIgnoreFile(result.gitignore, flags['dry-run'], 'DOM snapshots');
123
+ }
124
+ if (result.snapshotHooksMissing) {
125
+ this.log('\nwdio.conf.mjs already exists, so it was kept, and it does not capture DOM snapshots on failure. ' +
126
+ `To add that, copy the snapshot API loader and the onPrepare, before, afterStep and onComplete hooks from ${SNAPSHOT_HOOKS_DOCS}`);
113
127
  }
114
128
  const pageObjectExports = result.pageObjectExports;
115
129
  if (pageObjectExports?.action === 'added') {
@@ -129,26 +143,26 @@ export default class Init extends Command {
129
143
  }
130
144
  return result;
131
145
  }
132
- logForceignore(forceignore, dryRun) {
146
+ logIgnoreFile(file, dryRun, covers) {
133
147
  const where = {
134
148
  block: 'under the existing "# Ignore UTAM" comment',
135
149
  region: 'inside the customization region',
136
150
  end: 'at the end of the file',
137
151
  };
138
- const created = forceignore.action === 'created';
139
- if (forceignore.addedPatterns.length > 0) {
152
+ const created = file.action === 'created';
153
+ if (file.addedPatterns.length > 0) {
140
154
  const verb = dryRun ? (created ? 'Would create' : 'Would update') : created ? 'Created' : 'Updated';
141
- const placement = forceignore.placement && !created ? ` (${where[forceignore.placement]})` : '';
142
- this.log(`${verb} ${forceignore.path}: added ${forceignore.addedPatterns.join(', ')}${placement}`);
155
+ const placement = file.placement && !created ? ` (${where[file.placement]})` : '';
156
+ this.log(`${verb} ${file.path}: added ${file.addedPatterns.join(', ')}${placement}`);
143
157
  }
144
- else if (forceignore.action === 'unchanged') {
145
- this.log(`${forceignore.path} already covers UTAM artifacts.`);
158
+ else if (file.action === 'unchanged') {
159
+ this.log(`${file.path} already covers ${covers}.`);
146
160
  }
147
- else if (forceignore.action === 'skipped') {
148
- this.log(`Skipped ${forceignore.path}: no ${forceignore.path} or sfdx-project.json found. ` +
161
+ else if (file.action === 'skipped') {
162
+ this.log(`Skipped ${file.path}: no ${file.path} or sfdx-project.json found. ` +
149
163
  'Point --project-dir at the folder that contains sfdx-project.json, or add the UTAM rules yourself.');
150
164
  }
151
- for (const warning of forceignore.warnings) {
165
+ for (const warning of file.warnings) {
152
166
  this.warn(warning);
153
167
  }
154
168
  }
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type DomSnapshotMeta } from '@simplysf/simply-utam-core';
3
+ import { type Clipboard } from '../../../clipboard.js';
4
+ export type ImportResult = {
5
+ file: string;
6
+ meta: DomSnapshotMeta;
7
+ };
8
+ /**
9
+ * Save a DOM snapshot captured outside a test run, such as by the console snippet.
10
+ */
11
+ export default class SnapshotConsoleImport extends Command {
12
+ static summary: string;
13
+ static description: string;
14
+ static examples: string[];
15
+ static enableJsonFlag: boolean;
16
+ static flags: {
17
+ 'project-dir': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
18
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ label: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ };
21
+ /** The clipboard read without --file; tests replace it. */
22
+ clipboard: Clipboard;
23
+ /** Standard input, for `--file -`; tests replace it. */
24
+ stdin: NodeJS.ReadableStream;
25
+ run(): Promise<ImportResult>;
26
+ }
@@ -0,0 +1,96 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import * as fs from 'node:fs/promises';
17
+ import * as path from 'node:path';
18
+ import { Command, Flags } from '@oclif/core';
19
+ import { DomSnapshotError, MANUAL_RUN_ID, importDomSnapshot, writeDomSnapshot, } from '@simplysf/simply-utam-core';
20
+ import { ClipboardUnavailableError, systemClipboard } from '../../../clipboard.js';
21
+ import { BAD_INPUT, headerFor, projectDirFlag } from '../../../snapshot-flags.js';
22
+ async function readAll(stream) {
23
+ const chunks = [];
24
+ for await (const chunk of stream)
25
+ chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
26
+ return Buffer.concat(chunks).toString('utf8');
27
+ }
28
+ /**
29
+ * Save a DOM snapshot captured outside a test run, such as by the console snippet.
30
+ */
31
+ export default class SnapshotConsoleImport extends Command {
32
+ static summary = 'Save the DOM snapshot the console snippet copied, or any snapshot file.';
33
+ static description = 'Reads the clipboard by default, or --file (- for standard input). Checks the snapshot, saves it to .utam/snapshots/manual/ as manual--<label or number>.json and to latest.json, and prints the header every snapshot command prints. The rest of the snapshot is kept as captured.';
34
+ static examples = [
35
+ '<%= config.bin %> <%= command.id %> --label "account list"',
36
+ '<%= config.bin %> <%= command.id %> --file ~/Downloads/dom-snapshot-20261019T120000.json',
37
+ 'pbpaste | <%= config.bin %> <%= command.id %> --file -',
38
+ ];
39
+ static enableJsonFlag = true;
40
+ static flags = {
41
+ file: Flags.string({
42
+ summary: 'Snapshot file to read instead of the clipboard, relative to the current folder; - for standard input.',
43
+ }),
44
+ label: Flags.string({ summary: 'Label for the snapshot, used in its file name and header.' }),
45
+ ...projectDirFlag,
46
+ };
47
+ /** The clipboard read without --file; tests replace it. */
48
+ clipboard = systemClipboard();
49
+ /** Standard input, for `--file -`; tests replace it. */
50
+ stdin = process.stdin;
51
+ async run() {
52
+ const { flags } = await this.parse(SnapshotConsoleImport);
53
+ const source = flags.file === '-' ? 'standard input' : (flags.file ?? 'clipboard');
54
+ let text;
55
+ try {
56
+ text =
57
+ flags.file === '-'
58
+ ? await readAll(this.stdin)
59
+ : flags.file
60
+ ? await fs.readFile(path.resolve(flags.file), 'utf8')
61
+ : await this.clipboard.read();
62
+ }
63
+ catch (error) {
64
+ if (error instanceof ClipboardUnavailableError)
65
+ this.error(`Can't read the clipboard: ${error.message}. Use --file <path>, or --file - for standard input.`, {
66
+ exit: BAD_INPUT,
67
+ });
68
+ if (error.code === 'ENOENT')
69
+ this.error(`No such file: ${source}`, { exit: BAD_INPUT });
70
+ throw error;
71
+ }
72
+ if (!text.trim()) {
73
+ this.error(`The ${source} is empty. Run the snippet (\`npx simply-utam snapshot console snippet --copy\`) in the page's DevTools console first.`, { exit: BAD_INPUT });
74
+ }
75
+ let snapshot;
76
+ try {
77
+ snapshot = importDomSnapshot(text, { label: flags.label });
78
+ }
79
+ catch (error) {
80
+ if (error instanceof DomSnapshotError)
81
+ this.error(`The ${source} doesn't hold a DOM snapshot: ${error.message}`, { exit: BAD_INPUT });
82
+ throw error;
83
+ }
84
+ if (snapshot.meta.mask === 'full')
85
+ this.warn('This snapshot was captured with mask "full": it keeps record data.');
86
+ const projectDir = flags['project-dir'];
87
+ const file = await writeDomSnapshot(snapshot, { projectDir, runId: MANUAL_RUN_ID });
88
+ // From the snapshot in memory: the header doesn't show the package versions the file gains.
89
+ const header = headerFor(projectDir, path.join(projectDir, file), snapshot);
90
+ this.log(`DOM snapshot saved to ${file}`);
91
+ for (const line of header)
92
+ this.log(line);
93
+ return { file, meta: snapshot.meta };
94
+ }
95
+ }
96
+ //# sourceMappingURL=import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.js","sourceRoot":"","sources":["../../../../src/commands/snapshot/console/import.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type Clipboard } from '../../../clipboard.js';
3
+ export type SnippetResult = {
4
+ snippet: string;
5
+ copied: boolean;
6
+ };
7
+ /**
8
+ * Print the console snippet that captures a DOM snapshot, or copy it to the clipboard.
9
+ */
10
+ export default class SnapshotConsoleSnippet extends Command {
11
+ static summary: string;
12
+ static description: string;
13
+ static examples: string[];
14
+ static enableJsonFlag: boolean;
15
+ static flags: {
16
+ copy: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ mask: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ focus: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
19
+ 'max-nodes': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ 'max-hidden-nodes': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ };
22
+ /** The clipboard `--copy` writes to; tests replace it. */
23
+ clipboard: Clipboard;
24
+ run(): Promise<SnippetResult>;
25
+ }
@@ -0,0 +1,72 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { Command, Flags } from '@oclif/core';
17
+ import { domSnapshotSnippet } from '@simplysf/simply-utam-core';
18
+ import { ClipboardUnavailableError, systemClipboard } from '../../../clipboard.js';
19
+ import { BAD_INPUT } from '../../../snapshot-flags.js';
20
+ /**
21
+ * Print the console snippet that captures a DOM snapshot, or copy it to the clipboard.
22
+ */
23
+ export default class SnapshotConsoleSnippet extends Command {
24
+ static summary = "The snippet to paste into a browser's DevTools console to capture a DOM snapshot.";
25
+ static description = "Paste it into the console of the page to capture, with the console's context set to top. It runs the same walker a test run does, masks the page there, copies the snapshot JSON to the clipboard and prints a summary; save it with `snapshot console import`. Where the console's copy() isn't available, it downloads the JSON instead, for `import --file`. The mask is fixed in the snippet.";
26
+ static examples = [
27
+ '<%= config.bin %> <%= command.id %> --copy',
28
+ '<%= config.bin %> <%= command.id %> --mask structure --copy',
29
+ '<%= config.bin %> <%= command.id %> > snippet.js',
30
+ ];
31
+ static enableJsonFlag = true;
32
+ static flags = {
33
+ copy: Flags.boolean({ summary: 'Copy the snippet to the clipboard instead of printing it.', default: false }),
34
+ mask: Flags.string({
35
+ summary: 'Mask mode, from least kept to most. Defaults to UTAM_SNAPSHOT_MASK, then labels.',
36
+ options: ['structure', 'labels', 'aria', 'full'],
37
+ }),
38
+ focus: Flags.string({
39
+ summary: 'Where the walk starts: auto (the active Lightning page or console tab), none, or a CSS selector.',
40
+ default: 'auto',
41
+ }),
42
+ 'max-nodes': Flags.integer({ summary: 'Most visible nodes to capture.', min: 1 }),
43
+ 'max-hidden-nodes': Flags.integer({ summary: 'Most hidden nodes to capture.', min: 1 }),
44
+ };
45
+ /** The clipboard `--copy` writes to; tests replace it. */
46
+ clipboard = systemClipboard();
47
+ async run() {
48
+ const { flags } = await this.parse(SnapshotConsoleSnippet);
49
+ const snippet = domSnapshotSnippet({
50
+ mask: flags.mask,
51
+ focus: flags.focus,
52
+ maxNodes: flags['max-nodes'],
53
+ maxHiddenNodes: flags['max-hidden-nodes'],
54
+ log: (message) => this.warn(message),
55
+ });
56
+ if (!flags.copy) {
57
+ this.log(snippet);
58
+ return { snippet, copied: false };
59
+ }
60
+ try {
61
+ await this.clipboard.write(snippet);
62
+ }
63
+ catch (error) {
64
+ if (!(error instanceof ClipboardUnavailableError))
65
+ throw error;
66
+ this.error(`Can't copy the snippet: ${error.message}. Run without --copy to print it.`, { exit: BAD_INPUT });
67
+ }
68
+ this.log('Snippet copied. Paste it into the DevTools console of the page to capture (context: top), then run `npx simply-utam snapshot console import`.');
69
+ return { snippet, copied: true };
70
+ }
71
+ }
72
+ //# sourceMappingURL=snippet.js.map