@cronn/element-snapshot 0.9.0 → 0.10.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 +8 -3
- package/dist/browser-lib.d.ts +6 -3
- package/dist/browser-lib.global.js +23 -5
- package/dist/index.d.ts +14 -3
- package/dist/index.js +18 -7
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Element
|
|
1
|
+
# Element Snapshots
|
|
2
2
|
|
|
3
3
|
Element Snapshots are a custom snapshot implementation inspired by [Playwright's ARIA Snapshots](https://playwright.dev/docs/aria-snapshots#aria-snapshots) and the [Accessibility Object Model](https://wicg.github.io/aom/explainer.html). They cover additional properties, e.g. validation attributes like `required` or `invalid` on form inputs, and are optimized to be serialized as JSON.
|
|
4
4
|
|
|
@@ -82,13 +82,18 @@ Snapshot options can be passed when calling the snapshot function:
|
|
|
82
82
|
```ts
|
|
83
83
|
await expect(
|
|
84
84
|
snapshotElement(page.getByLabel("My select"), {
|
|
85
|
-
|
|
85
|
+
filter: (element) => element.role === "heading",
|
|
86
86
|
}),
|
|
87
87
|
).toMatchJsonFile({
|
|
88
|
-
name: "
|
|
88
|
+
name: "headings",
|
|
89
89
|
});
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
| Option | Default Value | Description |
|
|
93
|
+
| ------------------------ | ------------- | ------------------------------------------------------------------------------------ |
|
|
94
|
+
| `filter` | `() => true` | Include only elements in the snapshot for which the specified filter returns `true`. |
|
|
95
|
+
| `includeComboboxOptions` | `false` | Include combobox options in the snapshot. |
|
|
96
|
+
|
|
92
97
|
### Custom Snapshots
|
|
93
98
|
|
|
94
99
|
The function `snapshotElementRaw` provides the raw element snapshot in a strictly typed structure. This format is not well suited to be read by humans, but can be utilized to derive custom snapshot formats, e.g. for HTML tables.
|
package/dist/browser-lib.d.ts
CHANGED
|
@@ -66,6 +66,9 @@ interface HeadingAttributes {
|
|
|
66
66
|
level?: number;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
interface ImageSnapshot extends GenericElementSnapshot<"img"> {
|
|
70
|
+
}
|
|
71
|
+
|
|
69
72
|
interface LinkSnapshot extends GenericElementSnapshot<"link", LinkAttributes> {
|
|
70
73
|
}
|
|
71
74
|
interface LinkAttributes {
|
|
@@ -103,9 +106,9 @@ interface TextSnapshot {
|
|
|
103
106
|
|
|
104
107
|
type SnapshotTargetElement = HTMLElement | SVGElement;
|
|
105
108
|
type NodeRole = ElementRole | "text";
|
|
106
|
-
type ElementRole = "heading" | "link" | "button" | "option" | "tab" | "menuitem" | "columnheader" | "group" | "radiogroup" | "progressbar" | ContainerRole | InputRole | DialogRole;
|
|
109
|
+
type ElementRole = "heading" | "link" | "button" | "option" | "tab" | "menuitem" | "columnheader" | "group" | "radiogroup" | "progressbar" | "img" | ContainerRole | InputRole | DialogRole;
|
|
107
110
|
type NodeSnapshot = ElementSnapshot | TextSnapshot;
|
|
108
|
-
type ElementSnapshot = ContainerSnapshot | HeadingSnapshot | LinkSnapshot | ButtonSnapshot | InputSnapshot | ComboboxSnapshot | OptionSnapshot | DialogSnapshot | TabSnapshot | MenuitemSnapshot | ColumnheaderSnapshot | GroupSnapshot | RadiogroupSnapshot | ProgressbarSnapshot;
|
|
111
|
+
type ElementSnapshot = ContainerSnapshot | HeadingSnapshot | LinkSnapshot | ButtonSnapshot | InputSnapshot | ComboboxSnapshot | OptionSnapshot | DialogSnapshot | TabSnapshot | MenuitemSnapshot | ColumnheaderSnapshot | GroupSnapshot | RadiogroupSnapshot | ProgressbarSnapshot | ImageSnapshot;
|
|
109
112
|
interface GenericElementSnapshot<TRole extends NodeRole = NodeRole, TAttributes = unknown> {
|
|
110
113
|
role: TRole;
|
|
111
114
|
name?: string;
|
|
@@ -114,7 +117,7 @@ interface GenericElementSnapshot<TRole extends NodeRole = NodeRole, TAttributes
|
|
|
114
117
|
}
|
|
115
118
|
type SetValues<TSet> = TSet extends Set<infer TValue> ? TValue : never;
|
|
116
119
|
|
|
117
|
-
declare const CONTAINER_ROLES: Set<"paragraph" | "list" | "listitem" | "listbox" | "article" | "banner" | "complementary" | "contentinfo" | "form" | "main" | "navigation" | "region" | "search" | "term" | "definition" | "table" | "grid" | "rowgroup" | "row" | "rowheader" | "cell" | "gridcell" | "alert" | "menu" | "tablist" | "tabpanel">;
|
|
120
|
+
declare const CONTAINER_ROLES: Set<"paragraph" | "list" | "listitem" | "listbox" | "article" | "banner" | "complementary" | "contentinfo" | "form" | "main" | "navigation" | "region" | "search" | "term" | "definition" | "table" | "grid" | "rowgroup" | "row" | "rowheader" | "cell" | "gridcell" | "alert" | "menu" | "tablist" | "tabpanel" | "separator">;
|
|
118
121
|
interface ContainerSnapshot extends GenericElementSnapshot<ContainerRole> {
|
|
119
122
|
}
|
|
120
123
|
type ContainerRole = SetValues<typeof CONTAINER_ROLES>;
|
|
@@ -172,7 +172,8 @@
|
|
|
172
172
|
"alert",
|
|
173
173
|
"menu",
|
|
174
174
|
"tablist",
|
|
175
|
-
"tabpanel"
|
|
175
|
+
"tabpanel",
|
|
176
|
+
"separator"
|
|
176
177
|
]);
|
|
177
178
|
function snapshotContainer(role, element) {
|
|
178
179
|
const children = snapshotChildren(element);
|
|
@@ -242,7 +243,10 @@
|
|
|
242
243
|
th: resolveTableHeaderCellRole,
|
|
243
244
|
td: "cell",
|
|
244
245
|
fieldset: "group",
|
|
245
|
-
progress: "progressbar"
|
|
246
|
+
progress: "progressbar",
|
|
247
|
+
hr: "separator",
|
|
248
|
+
img: "img",
|
|
249
|
+
svg: "img"
|
|
246
250
|
};
|
|
247
251
|
var CONTEXT_DEPENDENT_ROLES = {
|
|
248
252
|
banner: isTopLevelBanner,
|
|
@@ -606,6 +610,20 @@
|
|
|
606
610
|
return numericAttribute(levelAsString);
|
|
607
611
|
}
|
|
608
612
|
|
|
613
|
+
// src/browser/image.ts
|
|
614
|
+
function snapshotImage(element) {
|
|
615
|
+
return {
|
|
616
|
+
role: "img",
|
|
617
|
+
name: resolveImageName(element)
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
function resolveImageName(element) {
|
|
621
|
+
if (element instanceof HTMLImageElement) {
|
|
622
|
+
return stringAttribute(element.alt);
|
|
623
|
+
}
|
|
624
|
+
return resolveAccessibleName(element, true);
|
|
625
|
+
}
|
|
626
|
+
|
|
609
627
|
// src/browser/link.ts
|
|
610
628
|
function snapshotLink(element) {
|
|
611
629
|
return {
|
|
@@ -696,7 +714,8 @@
|
|
|
696
714
|
columnheader: snapshotColumnheader,
|
|
697
715
|
group: snapshotGroup,
|
|
698
716
|
radiogroup: snapshotRadiogroup,
|
|
699
|
-
progressbar: snapshotProgressbar
|
|
717
|
+
progressbar: snapshotProgressbar,
|
|
718
|
+
img: snapshotImage
|
|
700
719
|
};
|
|
701
720
|
function snapshotElement(element) {
|
|
702
721
|
return snapshotNodeRecursive(element);
|
|
@@ -732,7 +751,6 @@
|
|
|
732
751
|
"style",
|
|
733
752
|
"script",
|
|
734
753
|
"noscript",
|
|
735
|
-
"img",
|
|
736
754
|
"picture",
|
|
737
755
|
"audio",
|
|
738
756
|
"video",
|
|
@@ -740,7 +758,7 @@
|
|
|
740
758
|
"template"
|
|
741
759
|
]);
|
|
742
760
|
function isSupportedElement(node) {
|
|
743
|
-
if (!(node instanceof HTMLElement)) {
|
|
761
|
+
if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
|
|
744
762
|
return false;
|
|
745
763
|
}
|
|
746
764
|
const tagName = getElementTagName(node);
|
package/dist/index.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ interface OptionAttributes {
|
|
|
48
48
|
selected?: boolean;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
declare const CONTAINER_ROLES: Set<"paragraph" | "list" | "listitem" | "listbox" | "article" | "banner" | "complementary" | "contentinfo" | "form" | "main" | "navigation" | "region" | "search" | "term" | "definition" | "table" | "grid" | "rowgroup" | "row" | "rowheader" | "cell" | "gridcell" | "alert" | "menu" | "tablist" | "tabpanel">;
|
|
51
|
+
declare const CONTAINER_ROLES: Set<"paragraph" | "list" | "listitem" | "listbox" | "article" | "banner" | "complementary" | "contentinfo" | "form" | "main" | "navigation" | "region" | "search" | "term" | "definition" | "table" | "grid" | "rowgroup" | "row" | "rowheader" | "cell" | "gridcell" | "alert" | "menu" | "tablist" | "tabpanel" | "separator">;
|
|
52
52
|
interface ContainerSnapshot extends GenericElementSnapshot<ContainerRole> {
|
|
53
53
|
}
|
|
54
54
|
type ContainerRole = SetValues<typeof CONTAINER_ROLES>;
|
|
@@ -73,6 +73,9 @@ interface HeadingAttributes {
|
|
|
73
73
|
level?: number;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
interface ImageSnapshot extends GenericElementSnapshot<"img"> {
|
|
77
|
+
}
|
|
78
|
+
|
|
76
79
|
interface LinkSnapshot extends GenericElementSnapshot<"link", LinkAttributes> {
|
|
77
80
|
}
|
|
78
81
|
interface LinkAttributes {
|
|
@@ -109,9 +112,9 @@ interface TextSnapshot {
|
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
type NodeRole = ElementRole | "text";
|
|
112
|
-
type ElementRole = "heading" | "link" | "button" | "option" | "tab" | "menuitem" | "columnheader" | "group" | "radiogroup" | "progressbar" | ContainerRole | InputRole | DialogRole;
|
|
115
|
+
type ElementRole = "heading" | "link" | "button" | "option" | "tab" | "menuitem" | "columnheader" | "group" | "radiogroup" | "progressbar" | "img" | ContainerRole | InputRole | DialogRole;
|
|
113
116
|
type NodeSnapshot = ElementSnapshot | TextSnapshot;
|
|
114
|
-
type ElementSnapshot = ContainerSnapshot | HeadingSnapshot | LinkSnapshot | ButtonSnapshot | InputSnapshot | ComboboxSnapshot | OptionSnapshot | DialogSnapshot | TabSnapshot | MenuitemSnapshot | ColumnheaderSnapshot | GroupSnapshot | RadiogroupSnapshot | ProgressbarSnapshot;
|
|
117
|
+
type ElementSnapshot = ContainerSnapshot | HeadingSnapshot | LinkSnapshot | ButtonSnapshot | InputSnapshot | ComboboxSnapshot | OptionSnapshot | DialogSnapshot | TabSnapshot | MenuitemSnapshot | ColumnheaderSnapshot | GroupSnapshot | RadiogroupSnapshot | ProgressbarSnapshot | ImageSnapshot;
|
|
115
118
|
interface GenericElementSnapshot<TRole extends NodeRole = NodeRole, TAttributes = unknown> {
|
|
116
119
|
role: TRole;
|
|
117
120
|
name?: string;
|
|
@@ -120,7 +123,15 @@ interface GenericElementSnapshot<TRole extends NodeRole = NodeRole, TAttributes
|
|
|
120
123
|
}
|
|
121
124
|
type SetValues<TSet> = TSet extends Set<infer TValue> ? TValue : never;
|
|
122
125
|
|
|
126
|
+
type SnapshotFilter = (node: NodeSnapshot) => boolean;
|
|
127
|
+
|
|
123
128
|
interface ElementSnapshotOptions {
|
|
129
|
+
/**
|
|
130
|
+
* Include only elements in the snapshot for which the specified filter returns `true`
|
|
131
|
+
*
|
|
132
|
+
* @default () => true
|
|
133
|
+
*/
|
|
134
|
+
filter?: SnapshotFilter;
|
|
124
135
|
/**
|
|
125
136
|
* Include combobox options in the snapshot
|
|
126
137
|
*
|
package/dist/index.js
CHANGED
|
@@ -43,19 +43,30 @@ var ElementSnapshotProxy = class {
|
|
|
43
43
|
|
|
44
44
|
// src/playwright/transformer.ts
|
|
45
45
|
var ElementSnapshotTransformer = class {
|
|
46
|
+
filter;
|
|
46
47
|
includeComboboxOptions;
|
|
47
48
|
constructor(options = {}) {
|
|
49
|
+
this.filter = options.filter ?? (() => true);
|
|
48
50
|
this.includeComboboxOptions = options.includeComboboxOptions ?? false;
|
|
49
51
|
}
|
|
50
52
|
transform(snapshots) {
|
|
51
|
-
const transformedSnapshots =
|
|
52
|
-
(snapshot) => this.transformSnapshotRecursive(snapshot)
|
|
53
|
-
);
|
|
53
|
+
const transformedSnapshots = this.filterAndTransformSnapshots(snapshots);
|
|
54
54
|
if (transformedSnapshots.length === 1) {
|
|
55
55
|
return transformedSnapshots.at(0);
|
|
56
56
|
}
|
|
57
57
|
return transformedSnapshots;
|
|
58
58
|
}
|
|
59
|
+
filterAndTransformSnapshots(snapshots) {
|
|
60
|
+
return snapshots.flatMap((snapshot) => {
|
|
61
|
+
if (this.filter(snapshot)) {
|
|
62
|
+
return this.transformSnapshotRecursive(snapshot);
|
|
63
|
+
}
|
|
64
|
+
if ("children" in snapshot) {
|
|
65
|
+
return this.filterAndTransformSnapshots(snapshot.children ?? []);
|
|
66
|
+
}
|
|
67
|
+
return [];
|
|
68
|
+
});
|
|
69
|
+
}
|
|
59
70
|
transformSnapshotRecursive(snapshot) {
|
|
60
71
|
if (snapshot.role === "text") {
|
|
61
72
|
return this.simplifyTextSnapshot(snapshot);
|
|
@@ -96,15 +107,15 @@ var ElementSnapshotTransformer = class {
|
|
|
96
107
|
const sparseAttributes = this.removeUndefinedProperties(
|
|
97
108
|
snapshot.attributes ?? {}
|
|
98
109
|
);
|
|
99
|
-
const transformedChildren =
|
|
100
|
-
|
|
110
|
+
const transformedChildren = this.filterAndTransformSnapshots(
|
|
111
|
+
snapshot?.children ?? []
|
|
101
112
|
);
|
|
102
|
-
const nameEqualsChildren = transformedChildren
|
|
113
|
+
const nameEqualsChildren = transformedChildren.length === 1 && snapshot.name === transformedChildren.at(0);
|
|
103
114
|
return {
|
|
104
115
|
role: snapshot.role,
|
|
105
116
|
name: snapshot.name,
|
|
106
117
|
attributes: sparseAttributes,
|
|
107
|
-
children: nameEqualsChildren ? void 0 : transformedChildren
|
|
118
|
+
children: nameEqualsChildren ? void 0 : transformedChildren.length === 0 ? void 0 : transformedChildren
|
|
108
119
|
};
|
|
109
120
|
}
|
|
110
121
|
simplifyComboboxSnapshot(snapshot) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cronn/element-snapshot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Element snapshots for Playwright",
|
|
5
5
|
"author": "cronn",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -16,17 +16,18 @@
|
|
|
16
16
|
"devEngines": {
|
|
17
17
|
"runtime": {
|
|
18
18
|
"name": "node",
|
|
19
|
-
"version": ">=24.
|
|
19
|
+
"version": ">=24.13 <25",
|
|
20
20
|
"onFail": "error"
|
|
21
21
|
},
|
|
22
22
|
"packageManager": {
|
|
23
23
|
"name": "pnpm",
|
|
24
|
-
"version": ">=10.
|
|
24
|
+
"version": ">=10.28.2 <11",
|
|
25
25
|
"onFail": "error"
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"type": "module",
|
|
29
29
|
"exports": {
|
|
30
|
+
"./package.json": "./package.json",
|
|
30
31
|
".": {
|
|
31
32
|
"types": "./dist/index.d.ts",
|
|
32
33
|
"default": "./dist/index.js"
|
|
@@ -39,16 +40,16 @@
|
|
|
39
40
|
"package-directory": "8.1.0"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
|
-
"@trivago/prettier-plugin-sort-imports": "6.0.
|
|
43
|
-
"@types/node": "24.10.
|
|
43
|
+
"@trivago/prettier-plugin-sort-imports": "6.0.2",
|
|
44
|
+
"@types/node": "24.10.9",
|
|
44
45
|
"eslint": "9.39.2",
|
|
45
46
|
"eslint-config-prettier": "10.1.8",
|
|
46
47
|
"eslint-plugin-unused-imports": "4.3.0",
|
|
47
|
-
"@playwright/test": "1.
|
|
48
|
-
"prettier": "3.
|
|
48
|
+
"@playwright/test": "1.58.0",
|
|
49
|
+
"prettier": "3.8.1",
|
|
49
50
|
"tsup": "8.5.1",
|
|
50
51
|
"typescript": "5.9.3",
|
|
51
|
-
"typescript-eslint": "8.
|
|
52
|
+
"typescript-eslint": "8.54.0",
|
|
52
53
|
"@cronn/shared-configs": "0.0.0"
|
|
53
54
|
},
|
|
54
55
|
"peerDependencies": {
|