@cronn/element-snapshot 0.12.0 → 0.14.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.
package/README.md CHANGED
@@ -25,6 +25,12 @@ pnpm add -D @cronn/element-snapshot
25
25
 
26
26
  ## Writing Tests
27
27
 
28
+ This package provides multiple snapshot formats for different use cases:
29
+
30
+ - **General-Purpose Snapshots**: Human-readable JSON format for all element types
31
+ - **Markdown Table Snapshots**: Specialized format for tables and grids ([documentation](docs/markdown-table-snapshot.md))
32
+ - **Custom Snapshots**: Build your own snapshot formats using raw element data and utility functions
33
+
28
34
  ### General-Purpose Snapshots
29
35
 
30
36
  The function `snapshotElement` provides a general-purpose snapshot including all supported roles and attributes. It can be used to achieve a high test coverage, but can become hard to read for complex HTML structures.
@@ -113,3 +119,13 @@ function transformToMarkdownTable(snapshot: Array<NodeSnapshot>): string {
113
119
  // transform table snapshot to markdown table
114
120
  }
115
121
  ```
122
+
123
+ #### Utility Functions for Custom Snapshots
124
+
125
+ | Function | Description |
126
+ | ---------------- | --------------------------------------------------------------------- |
127
+ | `filter` | Filters node snapshots based on the provided predicate function. |
128
+ | `filterByRole` | Filters node snapshots by the specified role. |
129
+ | `includeRole` | Includes only elements with the specified role. |
130
+ | `excludeRole` | Excludes elements with the specified role. |
131
+ | `getTextContent` | Aggregates and normalizes the text content of all provided snapshots. |
@@ -26,32 +26,57 @@
26
26
  //#endregion
27
27
  //#region src/browser/children.ts
28
28
  function snapshotChildren(node, excludedNodes = []) {
29
- const children = Array.from(node.childNodes).map((node) => snapshotNodeRecursive(node, excludedNodes)).filter((childSnapshot) => childSnapshot !== null).flat();
30
- return children.length > 0 ? children : void 0;
29
+ return Array.from(node.childNodes).map((childNode) => snapshotNodeRecursive(childNode, excludedNodes)).filter((childSnapshot) => childSnapshot !== null).flat();
31
30
  }
32
31
 
33
32
  //#endregion
34
- //#region src/utils/snapshot.ts
35
- function filterSnapshots(options) {
36
- const { filter, snapshots, recurse = false } = options;
33
+ //#region src/utils/guards.ts
34
+ function isTextSnapshot(snapshot) {
35
+ return hasRole("text", snapshot);
36
+ }
37
+ function hasRole(role, snapshot) {
38
+ if (typeof role === "string") return role === snapshot.role;
39
+ return role.includes(snapshot.role);
40
+ }
41
+
42
+ //#endregion
43
+ //#region src/utils/filter.ts
44
+ function filter(options) {
45
+ const { predicate, snapshots, recurse = false } = options;
37
46
  return snapshots.flatMap((snapshot) => {
38
- const isFilteredIn = filter(snapshot);
47
+ const isFilteredIn = predicate(snapshot);
39
48
  if (isFilteredIn && !recurse) return snapshot;
40
49
  if (!isFilteredIn && recurse) return [];
41
- const filteredChildren = hasChildren(snapshot) ? filterSnapshots({
42
- filter,
50
+ const filteredChildren = hasChildren(snapshot) ? filter({
51
+ predicate,
43
52
  snapshots: snapshot.children ?? [],
44
53
  recurse
45
54
  }) : [];
46
55
  if (!isFilteredIn) return filteredChildren;
56
+ if (isTextSnapshot(snapshot)) return snapshot;
47
57
  return {
48
58
  ...snapshot,
49
- children: filteredChildren.length > 0 ? filteredChildren : void 0
59
+ children: filteredChildren
50
60
  };
51
61
  });
52
62
  }
53
63
  function hasChildren(snapshot) {
54
- return "children" in snapshot && snapshot.children !== void 0 && snapshot.children.length > 0;
64
+ return "children" in snapshot && snapshot.children.length > 0;
65
+ }
66
+
67
+ //#endregion
68
+ //#region src/utils/text.ts
69
+ /**
70
+ * Aggregates and normalizes the text content of all provided snapshots
71
+ */
72
+ function getTextContent(snapshots) {
73
+ return normalizeText(filter({
74
+ snapshots,
75
+ predicate: isTextSnapshot
76
+ }).map((textNode) => textNode.name).join(" "));
77
+ }
78
+ function normalizeText(text) {
79
+ return text.replaceAll(/\s+/g, " ").trim();
55
80
  }
56
81
 
57
82
  //#endregion
@@ -67,7 +92,7 @@
67
92
  }
68
93
  function snapshotPresentationalChildren(node) {
69
94
  const accessibleTextContent = resolveAccessibleTextContent(node);
70
- if (accessibleTextContent === void 0 || accessibleTextContent.length === 0) return;
95
+ if (accessibleTextContent === void 0 || accessibleTextContent.length === 0) return [];
71
96
  return [{
72
97
  role: "text",
73
98
  name: accessibleTextContent
@@ -76,14 +101,8 @@
76
101
  function resolveAccessibleTextContent(node, excludedNodes = []) {
77
102
  const children = snapshotChildren(node, excludedNodes);
78
103
  if (children === void 0) return;
79
- const normalizedText = normalizeText(filterSnapshots({
80
- snapshots: children,
81
- filter: (element) => element.role === "text"
82
- }).map((textNode) => textNode.name).join(" "));
83
- return normalizedText.length > 0 ? normalizedText : void 0;
84
- }
85
- function normalizeText(text) {
86
- return text.replaceAll(/\s+/g, " ").trim();
104
+ const textContent = getTextContent(children);
105
+ return textContent.length > 0 ? textContent : void 0;
87
106
  }
88
107
 
89
108
  //#endregion
@@ -190,6 +209,7 @@
190
209
  return {
191
210
  role,
192
211
  name: resolveAccessibleName(element, false),
212
+ attributes: {},
193
213
  children
194
214
  };
195
215
  }
@@ -396,7 +416,8 @@
396
416
  value,
397
417
  checked: resolveChecked(element),
398
418
  ...snapshotCommonInputAttributes(element, value === void 0)
399
- }
419
+ },
420
+ children: []
400
421
  };
401
422
  }
402
423
  function snapshotTextareaElement(element) {
@@ -407,7 +428,8 @@
407
428
  attributes: {
408
429
  value,
409
430
  ...snapshotCommonInputAttributes(element, value === void 0)
410
- }
431
+ },
432
+ children: []
411
433
  };
412
434
  }
413
435
  function snapshotCommonInputAttributes(element, isEmpty) {
@@ -454,6 +476,7 @@
454
476
  value,
455
477
  ...snapshotCommonInputAttributes(element, value === void 0)
456
478
  },
479
+ children: [],
457
480
  options
458
481
  };
459
482
  }
@@ -512,6 +535,7 @@
512
535
  return {
513
536
  role: "group",
514
537
  name: resolveGroupName(element),
538
+ attributes: {},
515
539
  children: snapshotChildren(element)
516
540
  };
517
541
  }
@@ -552,7 +576,9 @@
552
576
  if (imageName === void 0) return null;
553
577
  return {
554
578
  role: "img",
555
- name: imageName
579
+ name: imageName,
580
+ attributes: {},
581
+ children: []
556
582
  };
557
583
  }
558
584
  function resolveImageName(element) {
@@ -608,6 +634,7 @@
608
634
  function snapshotSeparator(element) {
609
635
  return {
610
636
  role: "separator",
637
+ attributes: {},
611
638
  children: snapshotPresentationalChildren(element)
612
639
  };
613
640
  }
@@ -628,7 +655,7 @@
628
655
 
629
656
  //#endregion
630
657
  //#region src/browser/table.ts
631
- const SORT_ATTRIBUTES = new Set([
658
+ const SORT_TYPES = new Set([
632
659
  "ascending",
633
660
  "descending",
634
661
  "other"
@@ -637,7 +664,7 @@
637
664
  return {
638
665
  role: "columnheader",
639
666
  name: resolveAccessibleName(element),
640
- attributes: { sort: enumAttribute(element.ariaSort, SORT_ATTRIBUTES) },
667
+ attributes: { sort: enumAttribute(element.ariaSort, SORT_TYPES) },
641
668
  children: snapshotChildren(element)
642
669
  };
643
670
  }
package/dist/index.d.mts CHANGED
@@ -40,7 +40,7 @@ interface CommonInputAttributes extends InputStateAttributes, DiscribableAttribu
40
40
  //#endregion
41
41
  //#region src/browser/combobox.d.ts
42
42
  interface ComboboxSnapshot extends GenericElementSnapshot<"combobox", ComboboxAttributes> {
43
- options?: Array<OptionSnapshot>;
43
+ options: Array<OptionSnapshot>;
44
44
  }
45
45
  interface ComboboxAttributes extends CommonInputAttributes {
46
46
  value?: string | Array<string>;
@@ -102,10 +102,10 @@ interface TabAttributes extends DisableableAttributes, SelectableAttributes {}
102
102
  //#region src/browser/table.d.ts
103
103
  interface ColumnheaderSnapshot extends GenericElementSnapshot<"columnheader", ColumnheaderAttributes> {}
104
104
  interface ColumnheaderAttributes {
105
- sort?: SortAttribute;
105
+ sort?: SortType;
106
106
  }
107
- declare const SORT_ATTRIBUTES: Set<"ascending" | "descending" | "other">;
108
- type SortAttribute = SetValues<typeof SORT_ATTRIBUTES>;
107
+ declare const SORT_TYPES: Set<"ascending" | "descending" | "other">;
108
+ type SortType = SetValues<typeof SORT_TYPES>;
109
109
  //#endregion
110
110
  //#region src/browser/text.d.ts
111
111
  interface TextSnapshot {
@@ -118,32 +118,58 @@ type NodeRole = ElementRole | "text";
118
118
  type ElementRole = "heading" | "link" | "button" | "option" | "tab" | "menuitem" | "columnheader" | "group" | "radiogroup" | "progressbar" | "img" | "separator" | ContainerRole | InputRole | DialogRole;
119
119
  type NodeSnapshot = ElementSnapshot | TextSnapshot;
120
120
  type ElementSnapshot = ContainerSnapshot | HeadingSnapshot | LinkSnapshot | ButtonSnapshot | InputSnapshot | ComboboxSnapshot | OptionSnapshot | DialogSnapshot | TabSnapshot | MenuitemSnapshot | ColumnheaderSnapshot | GroupSnapshot | RadiogroupSnapshot | ProgressbarSnapshot | ImageSnapshot | SeparatorSnapshot;
121
- interface GenericElementSnapshot<TRole extends NodeRole = NodeRole, TAttributes = unknown> {
121
+ interface GenericElementSnapshot<TRole extends NodeRole = NodeRole, TAttributes = Record<string, unknown>> {
122
122
  role: TRole;
123
123
  name?: string;
124
- attributes?: TAttributes;
125
- children?: Array<NodeSnapshot>;
124
+ attributes: TAttributes;
125
+ children: Array<NodeSnapshot>;
126
126
  }
127
127
  type SetValues<TSet> = TSet extends Set<infer TValue> ? TValue : never;
128
128
  //#endregion
129
- //#region src/utils/snapshot.d.ts
130
- type SnapshotFilter = (snapshot: NodeSnapshot) => boolean;
131
- type GuardedSnapshotFilter<TSnapshot extends NodeSnapshot> = (snapshot: NodeSnapshot) => snapshot is TSnapshot;
132
- type FilteredSnapshot<TSnapshot extends NodeSnapshot> = TSnapshot extends GenericElementSnapshot ? TSnapshot & {
133
- children?: Array<TSnapshot>;
134
- } : TSnapshot;
135
- interface SnapshotFilterOptions {
136
- filter: SnapshotFilter;
137
- snapshots: Array<NodeSnapshot>;
138
- recurse: boolean;
129
+ //#region src/types/snapshot.d.ts
130
+ type SnapshotByRole<TRole extends NodeRole> = NodeSnapshot & {
131
+ role: TRole;
132
+ };
133
+ //#endregion
134
+ //#region src/utils/filter.d.ts
135
+ type FilterPredicate = (snapshot: NodeSnapshot) => boolean;
136
+ type GuardedFilterPredicate<TSnapshot extends NodeSnapshot> = (snapshot: NodeSnapshot) => snapshot is TSnapshot;
137
+ interface FilterOptions extends FilterBaseOptions {
138
+ /**
139
+ * Include only elements in the snapshot for which the specified predicate returns `true`
140
+ */
141
+ predicate: FilterPredicate;
142
+ }
143
+ interface GuardedFilterOptions<TSnapshot extends NodeSnapshot> extends FilterBaseOptions {
144
+ /**
145
+ * Include only elements in the snapshot for which the specified predicate returns `true`
146
+ */
147
+ predicate: GuardedFilterPredicate<TSnapshot>;
139
148
  }
140
- interface GuardedSnapshotFilterOptions<TSnapshot extends NodeSnapshot> {
141
- filter: GuardedSnapshotFilter<TSnapshot>;
149
+ interface FilterBaseOptions {
150
+ /**
151
+ * Array of snapshots to filter.
152
+ */
142
153
  snapshots: Array<NodeSnapshot>;
154
+ /**
155
+ * Recursively apply specified filter to children of filtered elements
156
+ *
157
+ * By default, recursion ends when the filter returns `true` for an element.
158
+ * Should be `true` for filters intended to remove specific elements recursively.
159
+ *
160
+ * @default false
161
+ */
143
162
  recurse?: boolean;
144
163
  }
145
- declare function filterSnapshots<TSnapshot extends NodeSnapshot = NodeSnapshot>(options: GuardedSnapshotFilterOptions<TSnapshot>): Array<FilteredSnapshot<TSnapshot>>;
146
- declare function filterSnapshots(options: SnapshotFilterOptions): Array<NodeSnapshot>;
164
+ /**
165
+ * Filters node snapshots based on the provided predicate function
166
+ */
167
+ declare function filter<TSnapshot extends NodeSnapshot = NodeSnapshot>(options: GuardedFilterOptions<TSnapshot>): Array<TSnapshot>;
168
+ declare function filter(options: FilterOptions): Array<NodeSnapshot>;
169
+ /**
170
+ * Filters node snapshots by a specific role
171
+ */
172
+ declare function filterByRole<TRole extends NodeRole>(role: TRole, snapshots: Array<NodeSnapshot>): Array<SnapshotByRole<TRole>>;
147
173
  //#endregion
148
174
  //#region src/playwright/snapshot.d.ts
149
175
  interface ElementSnapshotOptions {
@@ -152,7 +178,7 @@ interface ElementSnapshotOptions {
152
178
  *
153
179
  * @default () => true
154
180
  */
155
- filter?: SnapshotFilter;
181
+ filter?: FilterPredicate;
156
182
  /**
157
183
  * Recursively apply specified filter to children of filtered elements
158
184
  *
@@ -188,4 +214,20 @@ declare function snapshotElement(locator: Locator, options?: ElementSnapshotOpti
188
214
  */
189
215
  declare function snapshotElementRaw(locator: Locator): Promise<Array<NodeSnapshot>>;
190
216
  //#endregion
191
- export { type ButtonSnapshot, type ColumnheaderSnapshot, type ComboboxSnapshot, type ContainerSnapshot, type DialogSnapshot, type ElementRole, type ElementSnapshot, type GroupSnapshot, type HeadingSnapshot, type ImageSnapshot, type InputSnapshot, type LinkSnapshot, type MenuitemSnapshot, type NodeRole, type NodeSnapshot, type OptionSnapshot, type ProgressbarSnapshot, type RadiogroupSnapshot, type SeparatorSnapshot, type TabSnapshot, type TextSnapshot, filterSnapshots, snapshotElement, snapshotElementRaw };
217
+ //#region src/utils/predicates.d.ts
218
+ /**
219
+ * Include only elements with the specified role
220
+ */
221
+ declare function includeRole<TRole extends NodeRole>(role: TRole | Array<TRole>): GuardedFilterPredicate<SnapshotByRole<TRole>>;
222
+ /**
223
+ * Exclude elements with the specified role
224
+ */
225
+ declare function excludeRole<TRole extends NodeRole>(role: TRole | Array<TRole>): GuardedFilterPredicate<SnapshotByRole<TRole>>;
226
+ //#endregion
227
+ //#region src/utils/text.d.ts
228
+ /**
229
+ * Aggregates and normalizes the text content of all provided snapshots
230
+ */
231
+ declare function getTextContent(snapshots: Array<NodeSnapshot>): string;
232
+ //#endregion
233
+ export { type ButtonSnapshot, type ColumnheaderSnapshot, type ComboboxSnapshot, type ContainerSnapshot, type DialogSnapshot, type ElementRole, type ElementSnapshot, type GroupSnapshot, type HeadingSnapshot, type ImageSnapshot, type InputSnapshot, type LinkSnapshot, type MenuitemSnapshot, type NodeRole, type NodeSnapshot, type OptionSnapshot, type ProgressbarSnapshot, type RadiogroupSnapshot, type SeparatorSnapshot, type TabSnapshot, type TextSnapshot, excludeRole, filter, filterByRole, getTextContent, includeRole, snapshotElement, snapshotElementRaw };
package/dist/index.mjs CHANGED
@@ -29,27 +29,67 @@ var ElementSnapshotProxy = class {
29
29
  };
30
30
 
31
31
  //#endregion
32
- //#region src/utils/snapshot.ts
33
- function filterSnapshots(options) {
34
- const { filter, snapshots, recurse = false } = options;
32
+ //#region src/utils/guards.ts
33
+ function isTextSnapshot(snapshot) {
34
+ return hasRole("text", snapshot);
35
+ }
36
+ function hasRole(role, snapshot) {
37
+ if (typeof role === "string") return role === snapshot.role;
38
+ return role.includes(snapshot.role);
39
+ }
40
+ function isEmpty(value) {
41
+ if (typeof value === "string" || Array.isArray(value)) return value.length === 0;
42
+ return Object.keys(value).length === 0;
43
+ }
44
+
45
+ //#endregion
46
+ //#region src/utils/predicates.ts
47
+ /**
48
+ * Include only elements with the specified role
49
+ */
50
+ function includeRole(role) {
51
+ return (snapshot) => hasRole(role, snapshot);
52
+ }
53
+ /**
54
+ * Exclude elements with the specified role
55
+ */
56
+ function excludeRole(role) {
57
+ return (snapshot) => !hasRole(role, snapshot);
58
+ }
59
+
60
+ //#endregion
61
+ //#region src/utils/filter.ts
62
+ function filter(options) {
63
+ const { predicate, snapshots, recurse = false } = options;
35
64
  return snapshots.flatMap((snapshot) => {
36
- const isFilteredIn = filter(snapshot);
65
+ const isFilteredIn = predicate(snapshot);
37
66
  if (isFilteredIn && !recurse) return snapshot;
38
67
  if (!isFilteredIn && recurse) return [];
39
- const filteredChildren = hasChildren(snapshot) ? filterSnapshots({
40
- filter,
68
+ const filteredChildren = hasChildren(snapshot) ? filter({
69
+ predicate,
41
70
  snapshots: snapshot.children ?? [],
42
71
  recurse
43
72
  }) : [];
44
73
  if (!isFilteredIn) return filteredChildren;
74
+ if (isTextSnapshot(snapshot)) return snapshot;
45
75
  return {
46
76
  ...snapshot,
47
- children: filteredChildren.length > 0 ? filteredChildren : void 0
77
+ children: filteredChildren
48
78
  };
49
79
  });
50
80
  }
81
+ /**
82
+ * Filters node snapshots by a specific role
83
+ */
84
+ function filterByRole(role, snapshots) {
85
+ return filter({
86
+ predicate: includeRole(role),
87
+ recurse: false,
88
+ snapshots
89
+ });
90
+ }
51
91
  function hasChildren(snapshot) {
52
- return "children" in snapshot && snapshot.children !== void 0 && snapshot.children.length > 0;
92
+ return "children" in snapshot && snapshot.children.length > 0;
53
93
  }
54
94
 
55
95
  //#endregion
@@ -71,8 +111,8 @@ var ElementSnapshotTransformer = class {
71
111
  }
72
112
  filterSnapshots(snapshots) {
73
113
  if (this.filter === void 0) return snapshots;
74
- return filterSnapshots({
75
- filter: this.filter,
114
+ return filter({
115
+ predicate: this.filter,
76
116
  snapshots,
77
117
  recurse: this.recurseFilter
78
118
  });
@@ -81,7 +121,7 @@ var ElementSnapshotTransformer = class {
81
121
  return snapshots.map((snapshot) => this.transformSnapshotRecursive(snapshot));
82
122
  }
83
123
  transformSnapshotRecursive(snapshot) {
84
- if (snapshot.role === "text") return this.simplifyTextSnapshot(snapshot);
124
+ if (isTextSnapshot(snapshot)) return this.simplifyTextSnapshot(snapshot);
85
125
  return this.simplifyElementSnapshot(snapshot);
86
126
  }
87
127
  simplifyTextSnapshot(snapshot) {
@@ -104,25 +144,26 @@ var ElementSnapshotTransformer = class {
104
144
  if (snapshot.role === "combobox") return this.simplifyComboboxSnapshot(snapshot);
105
145
  const { role, name, attributes, children } = normalizedSnapshot;
106
146
  return this.transformedSnapshot(role, {
107
- name,
147
+ name: name.length === 0 ? void 0 : name,
108
148
  ...attributes,
109
- children
149
+ children: children.length === 0 ? void 0 : children
110
150
  });
111
151
  }
112
152
  normalizeElementSnapshot(snapshot) {
113
- const sparseAttributes = this.removeUndefinedProperties(snapshot.attributes ?? {});
114
- const transformedChildren = this.transformSnapshots(snapshot?.children ?? []);
115
- const nameEqualsChildren = transformedChildren.length === 1 && snapshot.name === transformedChildren.at(0);
153
+ const normalizedName = snapshot.name ?? "";
154
+ const sparseAttributes = this.removeUndefinedProperties(snapshot.attributes);
155
+ const transformedChildren = this.transformSnapshots(snapshot.children);
156
+ const nameEqualsChildren = transformedChildren.length === 1 && normalizedName === transformedChildren.at(0);
116
157
  return {
117
158
  role: snapshot.role,
118
- name: snapshot.name,
159
+ name: normalizedName,
119
160
  attributes: sparseAttributes,
120
- children: nameEqualsChildren ? void 0 : transformedChildren.length === 0 ? void 0 : transformedChildren
161
+ children: nameEqualsChildren ? [] : transformedChildren
121
162
  };
122
163
  }
123
164
  simplifyComboboxSnapshot(snapshot) {
124
165
  const { role, name, attributes } = this.normalizeElementSnapshot(snapshot);
125
- const transformedOptions = (snapshot.options ?? []).map((optionSnapshot) => this.simplifyElementSnapshot(optionSnapshot));
166
+ const transformedOptions = snapshot.options.map((optionSnapshot) => this.simplifyElementSnapshot(optionSnapshot));
126
167
  return this.transformedSnapshot(role, {
127
168
  name,
128
169
  ...attributes,
@@ -130,20 +171,20 @@ var ElementSnapshotTransformer = class {
130
171
  });
131
172
  }
132
173
  isEmpty(snapshot) {
133
- return snapshot.name === void 0 && (snapshot.attributes === void 0 || Object.keys(snapshot.attributes).length === 0) && (snapshot.children === void 0 || snapshot.children.length === 0);
174
+ return isEmpty(snapshot.name) && isEmpty(snapshot.attributes) && isEmpty(snapshot.children);
134
175
  }
135
176
  hasOnlyName(snapshot) {
136
- return snapshot.name !== void 0 && snapshot.attributes === void 0 && snapshot.children === void 0;
177
+ return !isEmpty(snapshot.name) && isEmpty(snapshot.attributes) && isEmpty(snapshot.children);
137
178
  }
138
179
  hasOnlyChildren(snapshot) {
139
- return snapshot.name === void 0 && snapshot.attributes === void 0 && snapshot.children !== void 0;
180
+ return isEmpty(snapshot.name) && isEmpty(snapshot.attributes) && !isEmpty(snapshot.children);
140
181
  }
141
182
  removeUndefinedProperties(attributes) {
142
183
  const sparseAttributes = {};
143
184
  Object.entries(attributes).forEach(([key, value]) => {
144
185
  if (value !== void 0) sparseAttributes[key] = value;
145
186
  });
146
- return Object.keys(sparseAttributes).length > 0 ? sparseAttributes : void 0;
187
+ return sparseAttributes;
147
188
  }
148
189
  transformedSnapshot(role, content) {
149
190
  return { [role]: content };
@@ -177,4 +218,19 @@ async function snapshotElementRaw(locator) {
177
218
  }
178
219
 
179
220
  //#endregion
180
- export { filterSnapshots, snapshotElement, snapshotElementRaw };
221
+ //#region src/utils/text.ts
222
+ /**
223
+ * Aggregates and normalizes the text content of all provided snapshots
224
+ */
225
+ function getTextContent(snapshots) {
226
+ return normalizeText(filter({
227
+ snapshots,
228
+ predicate: isTextSnapshot
229
+ }).map((textNode) => textNode.name).join(" "));
230
+ }
231
+ function normalizeText(text) {
232
+ return text.replaceAll(/\s+/g, " ").trim();
233
+ }
234
+
235
+ //#endregion
236
+ export { excludeRole, filter, filterByRole, getTextContent, includeRole, snapshotElement, snapshotElementRaw };
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@cronn/element-snapshot",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Element snapshots for Playwright",
5
+ "bugs": {
6
+ "url": "https://github.com/cronn/file-snapshots/issues"
7
+ },
5
8
  "author": "cronn",
6
9
  "license": "Apache-2.0",
7
10
  "repository": {
8
11
  "type": "git",
9
- "url": "https://github.com/cronn/file-snapshots.git",
12
+ "url": "git+https://github.com/cronn/file-snapshots.git",
10
13
  "directory": "packages/element-snapshot"
11
14
  },
12
15
  "homepage": "https://github.com/cronn/file-snapshots",
@@ -37,36 +40,41 @@
37
40
  "dist"
38
41
  ],
39
42
  "dependencies": {
43
+ "markdown-table": "3.0.4",
40
44
  "package-directory": "8.2.0"
41
45
  },
42
46
  "devDependencies": {
47
+ "@arethetypeswrong/core": "0.18.2",
43
48
  "@playwright/test": "1.58.2",
44
49
  "@trivago/prettier-plugin-sort-imports": "6.0.2",
45
- "@types/node": "24.10.12",
50
+ "@types/node": "24.11.0",
51
+ "@typescript/native-preview": "7.0.0-dev.20260302.1",
46
52
  "@vitest/coverage-v8": "4.0.18",
47
- "eslint": "9.39.2",
53
+ "eslint": "10.0.2",
48
54
  "eslint-config-prettier": "10.1.8",
49
- "eslint-plugin-unused-imports": "4.3.0",
55
+ "eslint-plugin-check-file": "3.3.1",
56
+ "eslint-plugin-unused-imports": "4.4.1",
50
57
  "prettier": "3.8.1",
58
+ "publint": "0.3.18",
51
59
  "tsdown": "0.20.3",
52
- "typescript": "5.9.3",
53
- "typescript-eslint": "8.54.0",
60
+ "typescript-eslint": "8.56.1",
54
61
  "vitest": "4.0.18",
55
- "@cronn/playwright-file-snapshots": "0.38.1",
56
62
  "@cronn/shared-configs": "0.0.0",
57
- "@cronn/test-utils": "0.0.0"
63
+ "@cronn/playwright-file-snapshots": "1.0.1",
64
+ "@cronn/test-utils": "0.0.0",
65
+ "@cronn/vitest-file-snapshots": "1.0.1"
58
66
  },
59
67
  "peerDependencies": {
60
68
  "@playwright/test": "^1.55"
61
69
  },
62
70
  "scripts": {
63
- "lint:check": "eslint src/ --max-warnings=0",
64
- "lint:fix": "eslint src/ --max-warnings=0 --fix",
71
+ "lint:check": "eslint src/ tests/ --max-warnings=0",
72
+ "lint:fix": "eslint src/ tests/ --max-warnings=0 --fix",
65
73
  "test:unit": "vitest run",
66
74
  "test:unit:coverage": "vitest run --coverage",
67
75
  "test:integration": "playwright test",
68
76
  "playwright:install": "playwright install chromium",
69
- "compile": "tsc",
77
+ "compile": "tsgo",
70
78
  "build": "tsdown"
71
79
  }
72
80
  }