playwright-intelligence-engine 2.0.4 → 2.1.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.
@@ -24,6 +24,8 @@ export interface TraceEntryDiscovery {
24
24
  /** All streams, ordered with explicit failure streams first. */
25
25
  traceEntryNames?: string[] | undefined;
26
26
  networkEntryNames?: string[] | undefined;
27
+ hasFailure?: boolean | undefined;
28
+ failureScore?: number | undefined;
27
29
  }
28
30
 
29
31
  export class ArchiveReader {
@@ -52,16 +54,23 @@ export class ArchiveReader {
52
54
  const text = entry.getData().toString('utf8');
53
55
  const lines = text.split(/\r?\n/);
54
56
  let failureScore = 0;
57
+ let hasRealError = false;
55
58
  for (const line of lines) {
56
59
  if (!line.includes('"type":"')) continue;
57
- if (line.includes('"type":"after"') && line.includes('"error"')) failureScore += 100;
58
- if (line.includes('"type":"error"') && (line.includes('"message"') || line.includes('"error"'))) failureScore += 80;
60
+ if (line.includes('"type":"after"') && line.includes('"error"')) {
61
+ failureScore += 100;
62
+ hasRealError = true;
63
+ }
64
+ if (line.includes('"type":"error"') && (line.includes('"message"') || line.includes('"error"'))) {
65
+ failureScore += 80;
66
+ hasRealError = true;
67
+ }
59
68
  if (line.includes('"type":"log"')) failureScore += 1;
60
69
  }
61
70
  if (path.basename(entry.entryName).toLowerCase() === 'test.trace' && failureScore > 0) {
62
71
  failureScore += 10;
63
72
  }
64
- return { entry, failureScore };
73
+ return { entry, failureScore, hasRealError };
65
74
  })
66
75
  .sort((a, b) => b.failureScore - a.failureScore || b.entry.header.size - a.entry.header.size);
67
76
 
@@ -88,11 +97,17 @@ export class ArchiveReader {
88
97
  bestNetwork = prefixMatch ? prefixMatch.entryName : networkCandidates.sort((a, b) => b.header.size - a.header.size)[0]?.entryName;
89
98
  }
90
99
 
100
+ const topCandidate = traceCandidates[0];
101
+ const hasFailure = Boolean(topCandidate?.hasRealError);
102
+ const topFailureScore = topCandidate?.failureScore ?? 0;
103
+
91
104
  return {
92
105
  traceEntryName: bestTrace,
93
106
  networkEntryName: bestNetwork,
94
107
  traceEntryNames: orderedTraceNames,
95
108
  networkEntryNames,
109
+ hasFailure,
110
+ failureScore: topFailureScore,
96
111
  };
97
112
  } catch {
98
113
  return {};
@@ -3,6 +3,13 @@ export type NodeSnapshot =
3
3
  | number
4
4
  | [string, Record<string, string>, ...NodeSnapshot[]];
5
5
 
6
+ export interface BoundingBox {
7
+ x: number;
8
+ y: number;
9
+ width: number;
10
+ height: number;
11
+ }
12
+
6
13
  export interface ResolvedElement {
7
14
  tag: string;
8
15
  attributes: Record<string, string>;
@@ -10,6 +17,7 @@ export interface ResolvedElement {
10
17
  children: ResolvedElement[];
11
18
  role?: string | undefined;
12
19
  accessibleName?: string | undefined;
20
+ boundingBox?: BoundingBox | undefined;
13
21
  }
14
22
 
15
23
  export type SnapshotCompleteness = 'COMPLETE' | 'PARTIAL' | 'UNAVAILABLE';
@@ -115,6 +123,17 @@ export class SnapshotResolver {
115
123
  inputValue ??
116
124
  textAccumulator.trim();
117
125
 
126
+ let boundingBox: BoundingBox | undefined;
127
+ const boxAttr = attributes['data-bbox'] ?? attributes['__playwright_box_'] ?? attributes['data-box'];
128
+ if (boxAttr) {
129
+ try {
130
+ const parts = boxAttr.split(',').map(Number);
131
+ if (parts.length === 4 && parts.every((p) => !isNaN(p))) {
132
+ boundingBox = { x: parts[0]!, y: parts[1]!, width: parts[2]!, height: parts[3]! };
133
+ }
134
+ } catch {}
135
+ }
136
+
118
137
  return {
119
138
  tag,
120
139
  attributes,
@@ -122,6 +141,7 @@ export class SnapshotResolver {
122
141
  children,
123
142
  role,
124
143
  accessibleName: accessibleName || undefined,
144
+ boundingBox,
125
145
  };
126
146
  }
127
147
 
@@ -222,6 +242,17 @@ export class SnapshotResolver {
222
242
  inputValue ??
223
243
  textAccumulator.trim();
224
244
 
245
+ let boundingBox: BoundingBox | undefined;
246
+ const boxAttr = attributes['data-bbox'] ?? attributes['__playwright_box_'] ?? attributes['data-box'];
247
+ if (boxAttr) {
248
+ try {
249
+ const parts = boxAttr.split(',').map(Number);
250
+ if (parts.length === 4 && parts.every((p) => !isNaN(p))) {
251
+ boundingBox = { x: parts[0]!, y: parts[1]!, width: parts[2]!, height: parts[3]! };
252
+ }
253
+ } catch {}
254
+ }
255
+
225
256
  return {
226
257
  tag,
227
258
  attributes,
@@ -229,6 +260,7 @@ export class SnapshotResolver {
229
260
  children,
230
261
  role,
231
262
  accessibleName: accessibleName || undefined,
263
+ boundingBox,
232
264
  };
233
265
  }
234
266