@intuned/browser-dev 0.1.9-dev.0 → 0.1.10-dev.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.
Files changed (29) hide show
  1. package/dist/ai/extractStructuredData.js +21 -27
  2. package/dist/ai/tests/testCreateMatchesMapping.spec.js +216 -0
  3. package/dist/ai/tests/testExtractStructuredData.spec.js +346 -0
  4. package/dist/ai/tests/testExtractStructuredDataDomMatchingIframes.spec.js +459 -0
  5. package/dist/ai/tests/testExtractStructuredDataUnit.spec.js +375 -0
  6. package/dist/ai/tests/testMatching.spec.js +342 -0
  7. package/dist/ai/tests/testValidateMatchesMapping.spec.js +265 -0
  8. package/dist/common/extendedTest.js +38 -30
  9. package/dist/common/frame_utils/frameTree.js +116 -0
  10. package/dist/common/frame_utils/getContentWithNestedIframes.js +13 -0
  11. package/dist/common/frame_utils/index.js +95 -0
  12. package/dist/common/frame_utils/stitchIframe.js +105 -0
  13. package/dist/{helpers → common}/frame_utils/tests/testFindAllIframes.spec.js +24 -15
  14. package/dist/common/frame_utils/tests/testGetContentWithNestedIframes.spec.js +241 -0
  15. package/dist/common/frame_utils/utils.js +91 -0
  16. package/dist/common/getSimplifiedHtml.js +20 -20
  17. package/dist/common/matching/matching.js +91 -16
  18. package/dist/common/tests/matching.test.js +225 -0
  19. package/dist/common/tests/testGetSimplifiedHtml.spec.js +324 -0
  20. package/dist/helpers/extractMarkdown.js +16 -7
  21. package/dist/helpers/tests/testExtractMarkdown.spec.js +29 -0
  22. package/dist/helpers/waitForDomSettled.js +4 -4
  23. package/dist/types/intuned-runtime.d.ts +6 -32
  24. package/package.json +1 -1
  25. package/dist/helpers/frame_utils/constants.js +0 -8
  26. package/dist/helpers/frame_utils/findAllIframes.js +0 -82
  27. package/dist/helpers/frame_utils/index.js +0 -44
  28. /package/dist/{helpers → common}/frame_utils/checkFrameAllowsAsyncScripts.js +0 -0
  29. /package/dist/{helpers → common}/frame_utils/getContainerFrame.js +0 -0
@@ -0,0 +1,342 @@
1
+ "use strict";
2
+
3
+ var _extendedTest = require("../../common/extendedTest");
4
+ var _playwright = require("playwright");
5
+ var _matching = require("../../common/matching/matching");
6
+ (0, _extendedTest.describe)("Matching Utility Functions", () => {
7
+ (0, _extendedTest.describe)("normalizeSpacing", () => {
8
+ (0, _extendedTest.test)("should replace multiple spaces with single space", async () => {
9
+ const result = (0, _matching.normalizeSpacing)({
10
+ text: "hello world"
11
+ });
12
+ (0, _extendedTest.expect)(result).toBe("hello world");
13
+ });
14
+ (0, _extendedTest.test)("should replace newlines and tabs with spaces", async () => {
15
+ const result = (0, _matching.normalizeSpacing)({
16
+ text: "hello\nworld\tthere"
17
+ });
18
+ (0, _extendedTest.expect)(result).toBe("hello world there");
19
+ });
20
+ (0, _extendedTest.test)("should handle mixed whitespace", async () => {
21
+ const result = (0, _matching.normalizeSpacing)({
22
+ text: " hello\n\n world\t\tthere "
23
+ });
24
+ (0, _extendedTest.expect)(result).toBe("hello world there");
25
+ });
26
+ (0, _extendedTest.test)("should handle empty string", async () => {
27
+ const result = (0, _matching.normalizeSpacing)({
28
+ text: ""
29
+ });
30
+ (0, _extendedTest.expect)(result).toBe("");
31
+ });
32
+ (0, _extendedTest.test)("should handle string with only whitespace", async () => {
33
+ const result = (0, _matching.normalizeSpacing)({
34
+ text: " \n\t "
35
+ });
36
+ (0, _extendedTest.expect)(result).toBe("");
37
+ });
38
+ });
39
+ (0, _extendedTest.describe)("removePunctuationAndSpaces", () => {
40
+ (0, _extendedTest.test)("should remove basic punctuation", async () => {
41
+ const result = (0, _matching.removePunctuationAndSpaces)({
42
+ s: "Hello, World!"
43
+ });
44
+ (0, _extendedTest.expect)(result).toBe("HelloWorld");
45
+ });
46
+ (0, _extendedTest.test)("should remove various punctuation marks", async () => {
47
+ const result = (0, _matching.removePunctuationAndSpaces)({
48
+ s: "Test@#$%^&*()_+-=[]{}|;':\",./<>?"
49
+ });
50
+ (0, _extendedTest.expect)(result).toBe("Test");
51
+ });
52
+ (0, _extendedTest.test)("should remove spaces", async () => {
53
+ const result = (0, _matching.removePunctuationAndSpaces)({
54
+ s: "hello world there"
55
+ });
56
+ (0, _extendedTest.expect)(result).toBe("helloworldthere");
57
+ });
58
+ (0, _extendedTest.test)("should handle mixed content", async () => {
59
+ const result = (0, _matching.removePunctuationAndSpaces)({
60
+ s: "Hello, World! How are you?"
61
+ });
62
+ (0, _extendedTest.expect)(result).toBe("HelloWorldHowareyou");
63
+ });
64
+ (0, _extendedTest.test)("should handle empty string", async () => {
65
+ const result = (0, _matching.removePunctuationAndSpaces)({
66
+ s: ""
67
+ });
68
+ (0, _extendedTest.expect)(result).toBe("");
69
+ });
70
+ });
71
+ (0, _extendedTest.describe)("rankMatch", () => {
72
+ (0, _extendedTest.test)("should rank exact match as HIGH", async () => {
73
+ const result = (0, _matching.rankMatch)({
74
+ original: "Hello World",
75
+ match: "Hello World"
76
+ });
77
+ (0, _extendedTest.expect)(result).toBe("HIGH");
78
+ });
79
+ (0, _extendedTest.test)("should rank case insensitive match as HIGH", async () => {
80
+ const result = (0, _matching.rankMatch)({
81
+ original: "Hello World",
82
+ match: "hello world"
83
+ });
84
+ (0, _extendedTest.expect)(result).toBe("HIGH");
85
+ });
86
+ (0, _extendedTest.test)("should rank match with punctuation differences as HIGH", async () => {
87
+ const result = (0, _matching.rankMatch)({
88
+ original: "Hello, World!",
89
+ match: "Hello World"
90
+ });
91
+ (0, _extendedTest.expect)(result).toBe("HIGH");
92
+ });
93
+ (0, _extendedTest.test)("should rank long string with high similarity as HIGH", async () => {
94
+ const original = "This is a very long string with more than twenty characters";
95
+ const match = "This is a very long string with more than twenty character";
96
+ const result = (0, _matching.rankMatch)({
97
+ original,
98
+ match
99
+ });
100
+ (0, _extendedTest.expect)(result).toBe("HIGH");
101
+ });
102
+ (0, _extendedTest.test)("should rank completely different strings as LOW", async () => {
103
+ const result = (0, _matching.rankMatch)({
104
+ original: "Hello World",
105
+ match: "Goodbye Universe"
106
+ });
107
+ (0, _extendedTest.expect)(result).toBe("LOW");
108
+ });
109
+ (0, _extendedTest.test)("should rank short string partial match as LOW", async () => {
110
+ const result = (0, _matching.rankMatch)({
111
+ original: "Hello",
112
+ match: "Hell"
113
+ });
114
+ (0, _extendedTest.expect)(result).toBe("LOW");
115
+ });
116
+ });
117
+ (0, _extendedTest.describe)("selectBestMatch", () => {
118
+ (0, _extendedTest.test)("should prefer exact match over fuzzy match", async () => {
119
+ const matches = [{
120
+ matched_value: "Hello Worlds",
121
+ match_mode: "fuzzy",
122
+ fuzzy_distance: 1,
123
+ match_source: "text_content",
124
+ xpath: "/html/body/div[1]"
125
+ }, {
126
+ matched_value: "Hello World",
127
+ match_mode: "full",
128
+ fuzzy_distance: 0,
129
+ match_source: "text_content",
130
+ xpath: "/html/body/div[2]"
131
+ }];
132
+ const result = (0, _matching.selectBestMatch)({
133
+ original: "Hello World",
134
+ matches
135
+ });
136
+ (0, _extendedTest.expect)(result === null || result === void 0 ? void 0 : result.matchText).toBe("Hello World");
137
+ });
138
+ (0, _extendedTest.test)("should select fuzzy match based on distance", async () => {
139
+ const matches = [{
140
+ matched_value: "Hello Worlds",
141
+ match_mode: "fuzzy",
142
+ fuzzy_distance: 1,
143
+ match_source: "text_content",
144
+ xpath: "/html/body/div[1]"
145
+ }, {
146
+ matched_value: "Hello World!",
147
+ match_mode: "fuzzy",
148
+ fuzzy_distance: 0.5,
149
+ match_source: "text_content",
150
+ xpath: "/html/body/div[2]"
151
+ }, {
152
+ matched_value: "Hello Worldz",
153
+ match_mode: "fuzzy",
154
+ fuzzy_distance: 2,
155
+ match_source: "text_content",
156
+ xpath: "/html/body/div[3]"
157
+ }];
158
+ const result = (0, _matching.selectBestMatch)({
159
+ original: "Hello World",
160
+ matches
161
+ });
162
+ (0, _extendedTest.expect)(result === null || result === void 0 ? void 0 : result.matchText).toBe("Hello World!");
163
+ });
164
+ (0, _extendedTest.test)("should return null when no good matches found", async () => {
165
+ const matches = [{
166
+ matched_value: "Completely Different",
167
+ match_mode: "fuzzy",
168
+ fuzzy_distance: 10,
169
+ match_source: "text_content",
170
+ xpath: "/html/body/div[1]"
171
+ }];
172
+ const result = (0, _matching.selectBestMatch)({
173
+ original: "Hello World",
174
+ matches
175
+ });
176
+ (0, _extendedTest.expect)(result).toBeNull();
177
+ });
178
+ (0, _extendedTest.test)("should return null with empty matches list", async () => {
179
+ const result = (0, _matching.selectBestMatch)({
180
+ original: "Hello World",
181
+ matches: []
182
+ });
183
+ (0, _extendedTest.expect)(result).toBeNull();
184
+ });
185
+ (0, _extendedTest.test)("should select first exact match when multiple exist", async () => {
186
+ const matches = [{
187
+ matched_value: "Hello World!",
188
+ match_mode: "full",
189
+ fuzzy_distance: 0,
190
+ match_source: "text_content",
191
+ xpath: "/html/body/div[1]"
192
+ }, {
193
+ matched_value: "Hello World",
194
+ match_mode: "full",
195
+ fuzzy_distance: 0,
196
+ match_source: "text_content",
197
+ xpath: "/html/body/div[2]"
198
+ }, {
199
+ matched_value: "HELLO WORLD",
200
+ match_mode: "full",
201
+ fuzzy_distance: 0,
202
+ match_source: "text_content",
203
+ xpath: "/html/body/div[3]"
204
+ }];
205
+ const result = (0, _matching.selectBestMatch)({
206
+ original: "Hello World",
207
+ matches
208
+ });
209
+ (0, _extendedTest.expect)(result === null || result === void 0 ? void 0 : result.matchText).toBe("Hello World!");
210
+ });
211
+ (0, _extendedTest.test)("should return null for low ranked fuzzy matches only", async () => {
212
+ const matches = [{
213
+ matched_value: "Completely Different Text",
214
+ match_mode: "fuzzy",
215
+ fuzzy_distance: 0.5,
216
+ match_source: "text_content",
217
+ xpath: "/html/body/div[1]"
218
+ }, {
219
+ matched_value: "Another Unrelated String",
220
+ match_mode: "fuzzy",
221
+ fuzzy_distance: 1,
222
+ match_source: "text_content",
223
+ xpath: "/html/body/div[2]"
224
+ }, {
225
+ matched_value: "Not Even Close",
226
+ match_mode: "fuzzy",
227
+ fuzzy_distance: 0.2,
228
+ match_source: "text_content",
229
+ xpath: "/html/body/div[3]"
230
+ }];
231
+ const result = (0, _matching.selectBestMatch)({
232
+ original: "Hello World",
233
+ matches
234
+ });
235
+ (0, _extendedTest.expect)(result).toBeNull();
236
+ });
237
+ (0, _extendedTest.test)("should handle partial match mode with exact matches", async () => {
238
+ const matches = [{
239
+ matched_value: "Hello",
240
+ match_mode: "partial",
241
+ fuzzy_distance: 0,
242
+ match_source: "text_content",
243
+ xpath: "/html/body/div[1]"
244
+ }, {
245
+ matched_value: "Hello Worlds",
246
+ match_mode: "fuzzy",
247
+ fuzzy_distance: 1,
248
+ match_source: "text_content",
249
+ xpath: "/html/body/div[2]"
250
+ }, {
251
+ matched_value: "World",
252
+ match_mode: "partial",
253
+ fuzzy_distance: 0,
254
+ match_source: "text_content",
255
+ xpath: "/html/body/div[3]"
256
+ }];
257
+ const result = (0, _matching.selectBestMatch)({
258
+ original: "Hello World",
259
+ matches
260
+ });
261
+ (0, _extendedTest.expect)(result === null || result === void 0 ? void 0 : result.matchText).toBe("Hello");
262
+ });
263
+ });
264
+ (0, _extendedTest.describe)("matchStringsWithDomContent", () => {
265
+ let browser;
266
+ let page;
267
+ (0, _extendedTest.beforeAll)(async () => {
268
+ browser = await _playwright.chromium.launch({
269
+ headless: true
270
+ });
271
+ });
272
+ (0, _extendedTest.afterAll)(async () => {
273
+ await browser.close();
274
+ });
275
+ (0, _extendedTest.beforeEach)(async () => {
276
+ page = await browser.newPage();
277
+ });
278
+ (0, _extendedTest.afterEach)(async () => {
279
+ await page.close();
280
+ });
281
+ (0, _extendedTest.test)("should match basic strings in DOM content", async () => {
282
+ await page.setContent(`
283
+ <html>
284
+ <body>
285
+ <h1>Hello World</h1>
286
+ <p>This is a test paragraph</p>
287
+ <span>Another test element</span>
288
+ </body>
289
+ </html>
290
+ `);
291
+ const stringsToMatch = ["Hello World", "test paragraph", "Another test"];
292
+ const matches = await (0, _matching.matchStringsWithDomContent)({
293
+ pageObject: page,
294
+ stringsList: stringsToMatch
295
+ });
296
+ (0, _extendedTest.expect)(matches).toHaveProperty("Hello World");
297
+ (0, _extendedTest.expect)(matches).toHaveProperty("test paragraph");
298
+ (0, _extendedTest.expect)(matches).toHaveProperty("Another test");
299
+ });
300
+ (0, _extendedTest.test)("should match strings in iframe content", async () => {
301
+ await page.setContent(`
302
+ <html>
303
+ <body>
304
+ <h1>Main Page Header</h1>
305
+ <p>Content before iframe</p>
306
+ <iframe id="test-iframe" srcdoc="<html><body><h2>Iframe Header</h2><p>Content inside iframe</p><span>Iframe text to match</span></body></html>"></iframe>
307
+ <p>Content after iframe</p>
308
+ </body>
309
+ </html>
310
+ `);
311
+ await page.waitForSelector("#test-iframe");
312
+ await page.frameLocator("#test-iframe").locator("body").waitFor();
313
+ const stringsToMatch = ["Main Page Header", "Content before iframe", "Iframe Header", "Content inside iframe", "Iframe text to match", "Content after iframe"];
314
+ const matches = await (0, _matching.matchStringsWithDomContent)({
315
+ pageObject: page,
316
+ stringsList: stringsToMatch
317
+ });
318
+ (0, _extendedTest.expect)(matches).toHaveProperty("Main Page Header");
319
+ (0, _extendedTest.expect)(matches["Main Page Header"].length).toBeGreaterThan(0);
320
+ (0, _extendedTest.expect)(matches).toHaveProperty("Content before iframe");
321
+ (0, _extendedTest.expect)(matches["Content before iframe"].length).toBeGreaterThan(0);
322
+ (0, _extendedTest.expect)(matches).toHaveProperty("Iframe Header");
323
+ (0, _extendedTest.expect)(matches["Iframe Header"].length).toBeGreaterThan(0);
324
+ (0, _extendedTest.expect)(matches).toHaveProperty("Content inside iframe");
325
+ (0, _extendedTest.expect)(matches["Content inside iframe"].length).toBeGreaterThan(0);
326
+ (0, _extendedTest.expect)(matches).toHaveProperty("Iframe text to match");
327
+ (0, _extendedTest.expect)(matches["Iframe text to match"].length).toBeGreaterThan(0);
328
+ (0, _extendedTest.expect)(matches).toHaveProperty("Content after iframe");
329
+ (0, _extendedTest.expect)(matches["Content after iframe"].length).toBeGreaterThan(0);
330
+ });
331
+ (0, _extendedTest.test)("should handle error gracefully", async () => {
332
+ const stringsToMatch = ["Some text"];
333
+ const matches = await (0, _matching.matchStringsWithDomContent)({
334
+ pageObject: page,
335
+ stringsList: stringsToMatch
336
+ });
337
+ (0, _extendedTest.expect)(matches).toEqual({
338
+ "Some text": []
339
+ });
340
+ });
341
+ });
342
+ });
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+
3
+ var _extendedTest = require("../../common/extendedTest");
4
+ var _playwright = require("playwright");
5
+ var _matching = require("../../common/matching/matching");
6
+ const PRODUCT_V1_HTML = `
7
+ <html>
8
+ <body>
9
+ <div class="product">
10
+ <h2 class="title">iPhone 14 Pro</h2>
11
+ <div class="price">$999</div>
12
+ <div class="stock">In Stock</div>
13
+ </div>
14
+ </body>
15
+ </html>
16
+ `;
17
+ const PRODUCT_V2_MODIFIED_CONTENT = `
18
+ <html>
19
+ <body>
20
+ <div class="product">
21
+ <h2 class="title">iPhone 15 Pro</h2>
22
+ <div class="price">$1099</div>
23
+ <div class="stock">In Stock</div>
24
+ </div>
25
+ </body>
26
+ </html>
27
+ `;
28
+ const PRODUCT_WITH_FOOTER = `
29
+ <html>
30
+ <body>
31
+ <div class="product">
32
+ <h2 class="title">iPhone 14 Pro</h2>
33
+ <div class="price">$999</div>
34
+ <div class="stock">In Stock</div>
35
+ </div>
36
+ <footer>
37
+ <div class="copyright">© 2024 Store</div>
38
+ <div class="links">Privacy | Terms</div>
39
+ </footer>
40
+ </body>
41
+ </html>
42
+ `;
43
+ const PRODUCT_WITH_HEADER = `
44
+ <html>
45
+ <body>
46
+ <header>
47
+ <div class="banner">New Arrival!</div>
48
+ </header>
49
+ <div class="product">
50
+ <h2 class="title">iPhone 14 Pro</h2>
51
+ <div class="price">$999</div>
52
+ <div class="stock">In Stock</div>
53
+ </div>
54
+ </body>
55
+ </html>
56
+ `;
57
+ const IFRAME_V1_HTML = `
58
+ <html>
59
+ <body>
60
+ <div class="product">
61
+ <h2 class="title">iPhone 14 Pro</h2>
62
+ <div class="price">$999</div>
63
+ </div>
64
+ <iframe id="details-frame" srcdoc='
65
+ <html>
66
+ <body>
67
+ <div class="stock">In Stock</div>
68
+ <div class="rating">4.5 stars</div>
69
+ </body>
70
+ </html>
71
+ '></iframe>
72
+ </body>
73
+ </html>
74
+ `;
75
+ const IFRAME_V2_MODIFIED_IFRAME = `
76
+ <html>
77
+ <body>
78
+ <div class="product">
79
+ <h2 class="title">iPhone 14 Pro</h2>
80
+ <div class="price">$999</div>
81
+ </div>
82
+ <iframe id="details-frame" srcdoc='
83
+ <html>
84
+ <body>
85
+ <div class="stock">Out of Stock</div>
86
+ <div class="rating">4.5 stars</div>
87
+ </body>
88
+ </html>
89
+ '></iframe>
90
+ </body>
91
+ </html>
92
+ `;
93
+ const IFRAME_WITH_EXTRA_CONTENT = `
94
+ <html>
95
+ <body>
96
+ <div class="product">
97
+ <h2 class="title">iPhone 14 Pro</h2>
98
+ <div class="price">$999</div>
99
+ </div>
100
+ <iframe id="details-frame" srcdoc='
101
+ <html>
102
+ <body>
103
+ <div class="stock">In Stock</div>
104
+ <div class="rating">4.5 stars</div>
105
+ <div class="reviews">100+ reviews</div>
106
+ </body>
107
+ </html>
108
+ '></iframe>
109
+ </body>
110
+ </html>
111
+ `;
112
+ (0, _extendedTest.describe)("validateMatchesMapping", () => {
113
+ let browser;
114
+ let page;
115
+ (0, _extendedTest.beforeAll)(async () => {
116
+ browser = await _playwright.chromium.launch({
117
+ headless: true
118
+ });
119
+ });
120
+ (0, _extendedTest.afterAll)(async () => {
121
+ await browser.close();
122
+ });
123
+ (0, _extendedTest.beforeEach)(async () => {
124
+ page = await browser.newPage();
125
+ });
126
+ (0, _extendedTest.afterEach)(async () => {
127
+ await page.close();
128
+ });
129
+ (0, _extendedTest.test)("should return true when content hasn't changed", async () => {
130
+ await page.setContent(PRODUCT_V1_HTML);
131
+ const extractedData = {
132
+ title: "iPhone 14 Pro",
133
+ price: "$999",
134
+ stock: "In Stock"
135
+ };
136
+ const cachedMapping = await (0, _matching.createMatchesMapping)(page, extractedData);
137
+ const isValid = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
138
+ (0, _extendedTest.expect)(isValid).toBe(true);
139
+ });
140
+ (0, _extendedTest.test)("should return false when extracted content changes", async () => {
141
+ await page.setContent(PRODUCT_V1_HTML);
142
+ const extractedData = {
143
+ title: "iPhone 14 Pro",
144
+ price: "$999"
145
+ };
146
+ const cachedMapping = await (0, _matching.createMatchesMapping)(page, extractedData);
147
+ await page.setContent(PRODUCT_V2_MODIFIED_CONTENT);
148
+ const isValid = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
149
+ (0, _extendedTest.expect)(isValid).toBe(false);
150
+ });
151
+ (0, _extendedTest.test)("should return true when irrelevant elements are added (footer)", async () => {
152
+ await page.setContent(PRODUCT_V1_HTML);
153
+ const extractedData = {
154
+ title: "iPhone 14 Pro",
155
+ price: "$999"
156
+ };
157
+ const cachedMapping = await (0, _matching.createMatchesMapping)(page, extractedData);
158
+ await page.setContent(PRODUCT_WITH_FOOTER);
159
+ const isValid = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
160
+ (0, _extendedTest.expect)(isValid).toBe(true);
161
+ });
162
+ (0, _extendedTest.test)("should return true when header is added at top of page", async () => {
163
+ await page.setContent(PRODUCT_V1_HTML);
164
+ const extractedData = {
165
+ title: "iPhone 14 Pro",
166
+ price: "$999"
167
+ };
168
+ const cachedMapping = await (0, _matching.createMatchesMapping)(page, extractedData);
169
+ await page.setContent(PRODUCT_WITH_HEADER);
170
+ const isValid = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
171
+ (0, _extendedTest.expect)(isValid).toBe(true);
172
+ });
173
+ (0, _extendedTest.test)("should work with unchanged iframe content", async () => {
174
+ await page.setContent(IFRAME_V1_HTML);
175
+ await page.waitForSelector("#details-frame");
176
+ const extractedData = {
177
+ title: "iPhone 14 Pro",
178
+ price: "$999",
179
+ stock: "In Stock",
180
+ rating: "4.5 stars"
181
+ };
182
+ const cachedMapping = await (0, _matching.createMatchesMapping)(page, extractedData);
183
+ const isValid = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
184
+ (0, _extendedTest.expect)(isValid).toBe(true);
185
+ });
186
+ (0, _extendedTest.test)("should return false when iframe content changes", async () => {
187
+ await page.setContent(IFRAME_V1_HTML);
188
+ await page.waitForSelector("#details-frame");
189
+ const extractedData = {
190
+ stock: "In Stock",
191
+ rating: "4.5 stars"
192
+ };
193
+ const cachedMapping = await (0, _matching.createMatchesMapping)(page, extractedData);
194
+ await page.setContent(IFRAME_V2_MODIFIED_IFRAME);
195
+ await page.waitForSelector("#details-frame");
196
+ const isValid = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
197
+ (0, _extendedTest.expect)(isValid).toBe(false);
198
+ });
199
+ (0, _extendedTest.test)("should return true when extra content is added to iframe", async () => {
200
+ await page.setContent(IFRAME_V1_HTML);
201
+ await page.waitForSelector("#details-frame");
202
+ const extractedData = {
203
+ stock: "In Stock",
204
+ rating: "4.5 stars"
205
+ };
206
+ const cachedMapping = await (0, _matching.createMatchesMapping)(page, extractedData);
207
+ await page.setContent(IFRAME_WITH_EXTRA_CONTENT);
208
+ await page.waitForSelector("#details-frame");
209
+ const isValid = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
210
+ (0, _extendedTest.expect)(isValid).toBe(true);
211
+ });
212
+ (0, _extendedTest.test)("should return false when only some extracted values change", async () => {
213
+ await page.setContent(PRODUCT_V1_HTML);
214
+ const extractedData = {
215
+ title: "iPhone 14 Pro",
216
+ price: "$999",
217
+ stock: "In Stock"
218
+ };
219
+ const cachedMapping = await (0, _matching.createMatchesMapping)(page, extractedData);
220
+ await page.setContent(PRODUCT_V2_MODIFIED_CONTENT);
221
+ const isValid = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
222
+ (0, _extendedTest.expect)(isValid).toBe(false);
223
+ });
224
+ (0, _extendedTest.test)("should return true for empty mapping", async () => {
225
+ await page.setContent(PRODUCT_V1_HTML);
226
+ const emptyMapping = {};
227
+ const isValid = await (0, _matching.validateMatchesMapping)(page, emptyMapping);
228
+ (0, _extendedTest.expect)(isValid).toBe(true);
229
+ });
230
+ (0, _extendedTest.test)("should return false when page content is completely different", async () => {
231
+ await page.setContent(PRODUCT_V1_HTML);
232
+ const extractedData = {
233
+ title: "iPhone 14 Pro",
234
+ price: "$999"
235
+ };
236
+ const cachedMapping = await (0, _matching.createMatchesMapping)(page, extractedData);
237
+ const differentPage = `
238
+ <html>
239
+ <body>
240
+ <div class="article">
241
+ <h1>About Us</h1>
242
+ <p>We are a company...</p>
243
+ </div>
244
+ </body>
245
+ </html>
246
+ `;
247
+ await page.setContent(differentPage);
248
+ const isValid = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
249
+ (0, _extendedTest.expect)(isValid).toBe(false);
250
+ });
251
+ (0, _extendedTest.test)("should be consistent across multiple calls", async () => {
252
+ await page.setContent(PRODUCT_V1_HTML);
253
+ const extractedData = {
254
+ title: "iPhone 14 Pro",
255
+ price: "$999"
256
+ };
257
+ const cachedMapping = await (0, _matching.createMatchesMapping)(page, extractedData);
258
+ const isValid1 = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
259
+ const isValid2 = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
260
+ const isValid3 = await (0, _matching.validateMatchesMapping)(page, cachedMapping);
261
+ (0, _extendedTest.expect)(isValid1).toBe(true);
262
+ (0, _extendedTest.expect)(isValid2).toBe(true);
263
+ (0, _extendedTest.expect)(isValid3).toBe(true);
264
+ });
265
+ });