@chen-rmag/ai-runner 0.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.
Files changed (102) hide show
  1. package/README.md +263 -0
  2. package/SUMMARY_USAGE.md +359 -0
  3. package/TOOLS_INTEGRATION_SUMMARY.md +206 -0
  4. package/dist/agents/error-analyzer.d.ts +62 -0
  5. package/dist/agents/error-analyzer.d.ts.map +1 -0
  6. package/dist/agents/error-analyzer.js +168 -0
  7. package/dist/agents/error-analyzer.js.map +1 -0
  8. package/dist/agents/heal-agent.d.ts +30 -0
  9. package/dist/agents/heal-agent.d.ts.map +1 -0
  10. package/dist/agents/heal-agent.js +76 -0
  11. package/dist/agents/heal-agent.js.map +1 -0
  12. package/dist/agents/healer.d.ts +73 -0
  13. package/dist/agents/healer.d.ts.map +1 -0
  14. package/dist/agents/healer.js +538 -0
  15. package/dist/agents/healer.js.map +1 -0
  16. package/dist/agents/langgraph-agent.d.ts +44 -0
  17. package/dist/agents/langgraph-agent.d.ts.map +1 -0
  18. package/dist/agents/langgraph-agent.js +328 -0
  19. package/dist/agents/langgraph-agent.js.map +1 -0
  20. package/dist/agents/react-agent.d.ts +52 -0
  21. package/dist/agents/react-agent.d.ts.map +1 -0
  22. package/dist/agents/react-agent.js +262 -0
  23. package/dist/agents/react-agent.js.map +1 -0
  24. package/dist/agents/tools/form.d.ts +22 -0
  25. package/dist/agents/tools/form.d.ts.map +1 -0
  26. package/dist/agents/tools/form.js +134 -0
  27. package/dist/agents/tools/form.js.map +1 -0
  28. package/dist/agents/tools/index.d.ts +13 -0
  29. package/dist/agents/tools/index.d.ts.map +1 -0
  30. package/dist/agents/tools/index.js +33 -0
  31. package/dist/agents/tools/index.js.map +1 -0
  32. package/dist/agents/tools/navigate.d.ts +22 -0
  33. package/dist/agents/tools/navigate.d.ts.map +1 -0
  34. package/dist/agents/tools/navigate.js +74 -0
  35. package/dist/agents/tools/navigate.js.map +1 -0
  36. package/dist/agents/tools/snapshot.d.ts +22 -0
  37. package/dist/agents/tools/snapshot.d.ts.map +1 -0
  38. package/dist/agents/tools/snapshot.js +110 -0
  39. package/dist/agents/tools/snapshot.js.map +1 -0
  40. package/dist/agents/tools/verify.d.ts +34 -0
  41. package/dist/agents/tools/verify.d.ts.map +1 -0
  42. package/dist/agents/tools/verify.js +169 -0
  43. package/dist/agents/tools/verify.js.map +1 -0
  44. package/dist/agents/tools/wait.d.ts +22 -0
  45. package/dist/agents/tools/wait.d.ts.map +1 -0
  46. package/dist/agents/tools/wait.js +104 -0
  47. package/dist/agents/tools/wait.js.map +1 -0
  48. package/dist/agents/types.d.ts +51 -0
  49. package/dist/agents/types.d.ts.map +1 -0
  50. package/dist/agents/types.js +6 -0
  51. package/dist/agents/types.js.map +1 -0
  52. package/dist/core/ai-heal.d.ts +89 -0
  53. package/dist/core/ai-heal.d.ts.map +1 -0
  54. package/dist/core/ai-heal.js +468 -0
  55. package/dist/core/ai-heal.js.map +1 -0
  56. package/dist/core/execution-engine.d.ts +16 -0
  57. package/dist/core/execution-engine.d.ts.map +1 -0
  58. package/dist/core/execution-engine.js +44 -0
  59. package/dist/core/execution-engine.js.map +1 -0
  60. package/dist/core/runner.d.ts +195 -0
  61. package/dist/core/runner.d.ts.map +1 -0
  62. package/dist/core/runner.js +658 -0
  63. package/dist/core/runner.js.map +1 -0
  64. package/dist/index.d.ts +8 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +11 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/types/external.d.ts +6 -0
  69. package/dist/types/external.d.ts.map +1 -0
  70. package/dist/types/external.js +7 -0
  71. package/dist/types/external.js.map +1 -0
  72. package/dist/types/index.d.ts +153 -0
  73. package/dist/types/index.d.ts.map +1 -0
  74. package/dist/types/index.js +26 -0
  75. package/dist/types/index.js.map +1 -0
  76. package/dist/utils/object-registry.d.ts +48 -0
  77. package/dist/utils/object-registry.d.ts.map +1 -0
  78. package/dist/utils/object-registry.js +133 -0
  79. package/dist/utils/object-registry.js.map +1 -0
  80. package/package.json +37 -0
  81. package/playwright.config.ts +38 -0
  82. package/src/agents/heal-agent.ts +85 -0
  83. package/src/agents/healer.ts +619 -0
  84. package/src/agents/tools/EXAMPLES.md +347 -0
  85. package/src/agents/tools/README.md +207 -0
  86. package/src/agents/tools/form.ts +138 -0
  87. package/src/agents/tools/index.ts +29 -0
  88. package/src/agents/tools/navigate.ts +69 -0
  89. package/src/agents/tools/snapshot.ts +109 -0
  90. package/src/agents/tools/verify.ts +168 -0
  91. package/src/agents/tools/wait.ts +103 -0
  92. package/src/agents/types.ts +79 -0
  93. package/src/core/runner.ts +756 -0
  94. package/src/index.ts +29 -0
  95. package/src/types/external.ts +7 -0
  96. package/src/types/index.ts +200 -0
  97. package/tests/agent/test-heal-agent.spec.ts +81 -0
  98. package/tests/tools/README.md +227 -0
  99. package/tests/tools/TEST_SUMMARY.md +214 -0
  100. package/tests/tools/quick-test.ts +88 -0
  101. package/tests/tools/tools.test.ts +491 -0
  102. package/tsconfig.json +22 -0
@@ -0,0 +1,491 @@
1
+ /**
2
+ * LangChain 兼容的 Playwright 工具测试
3
+ */
4
+
5
+ import { test, expect } from '@playwright/test';
6
+ import { getAllTools } from '../../src/agents/tools';
7
+ import { chromium, type BrowserContext } from 'playwright';
8
+
9
+ test.describe('LangChain Playwright Tools', () => {
10
+ let context: BrowserContext;
11
+ let page: any;
12
+
13
+ test.beforeAll(async () => {
14
+ const browser = await chromium.launch();
15
+ context = await browser.newContext();
16
+ page = await context.newPage();
17
+ });
18
+
19
+ test.afterAll(async () => {
20
+ await context.close();
21
+ });
22
+
23
+ test.describe('Navigation Tools', () => {
24
+ test('browser_navigate - should navigate to URL', async () => {
25
+ const tools = getAllTools(page);
26
+ const navigateTool = tools.find(t => t.name === 'browser_navigate');
27
+
28
+ expect(navigateTool).toBeDefined();
29
+
30
+ const result = await navigateTool!.invoke('https://example.com');
31
+ expect(result).toContain('Navigated to');
32
+ expect(page.url()).toBe('https://example.com');
33
+ });
34
+
35
+ test('browser_navigate_back - should go back', async () => {
36
+ const tools = getAllTools(page);
37
+ const navigateTool = tools.find(t => t.name === 'browser_navigate');
38
+ const backTool = tools.find(t => t.name === 'browser_navigate_back');
39
+
40
+ await navigateTool!.invoke('https://example.com');
41
+ await navigateTool!.invoke('https://example.com/about');
42
+
43
+ const result = await backTool!.invoke('');
44
+ expect(result).toContain('Navigated back');
45
+ });
46
+
47
+ test('browser_navigate_forward - should go forward', async () => {
48
+ const tools = getAllTools(page);
49
+ const navigateTool = tools.find(t => t.name === 'browser_navigate');
50
+ const forwardTool = tools.find(t => t.name === 'browser_navigate_forward');
51
+
52
+ await navigateTool!.invoke('https://example.com');
53
+ await navigateTool!.invoke('https://example.com/about');
54
+ await page.goBack();
55
+
56
+ const result = await forwardTool!.invoke('');
57
+ expect(result).toContain('Navigated forward');
58
+ });
59
+ });
60
+
61
+ test.describe('Snapshot Tools', () => {
62
+ test.beforeEach(async () => {
63
+ await page.goto('https://example.com');
64
+ });
65
+
66
+ test('browser_snapshot - should get page snapshot', async () => {
67
+ const tools = getAllTools(page);
68
+ const snapshotTool = tools.find(t => t.name === 'browser_snapshot');
69
+
70
+ const result = await snapshotTool!.invoke('');
71
+ expect(result).toContain('Page accessibility snapshot');
72
+ expect(result.length).toBeGreaterThan(0);
73
+ });
74
+
75
+ test('browser_click - should click element by text', async () => {
76
+ const tools = getAllTools(page);
77
+ const clickTool = tools.find(t => t.name === 'browser_click');
78
+
79
+ const result = await clickTool!.invoke('More information');
80
+ expect(result).toContain('Clicked');
81
+ });
82
+
83
+ test('browser_click - should click element by CSS selector', async () => {
84
+ const tools = getAllTools(page);
85
+ const clickTool = tools.find(t => t.name === 'browser_click');
86
+
87
+ const result = await clickTool!.invoke('a');
88
+ expect(result).toContain('Clicked');
89
+ });
90
+
91
+ test('browser_click - should try multiple selectors', async () => {
92
+ const tools = getAllTools(page);
93
+ const clickTool = tools.find(t => t.name === 'browser_click');
94
+
95
+ // 提供多个选择器,至少有一个应该成功
96
+ const result = await clickTool!.invoke('nonexistent, a, h1');
97
+ expect(result).toContain('Clicked');
98
+ });
99
+
100
+ test('browser_hover - should hover over element', async () => {
101
+ const tools = getAllTools(page);
102
+ const hoverTool = tools.find(t => t.name === 'browser_hover');
103
+
104
+ const result = await hoverTool!.invoke('h1');
105
+ expect(result).toContain('Hovered');
106
+ });
107
+ });
108
+
109
+ test.describe('Form Tools', () => {
110
+ test.beforeEach(async () => {
111
+ // 创建一个简单的测试表单页面
112
+ await page.setContent(`
113
+ <!DOCTYPE html>
114
+ <html>
115
+ <body>
116
+ <form id="test-form">
117
+ <label for="username">Username:</label>
118
+ <input type="text" id="username" name="username" />
119
+
120
+ <label for="email">Email:</label>
121
+ <input type="email" id="email" name="email" placeholder="Enter email" />
122
+
123
+ <label for="country">Country:</label>
124
+ <select id="country" name="country">
125
+ <option value="">Select...</option>
126
+ <option value="china">China</option>
127
+ <option value="usa">USA</option>
128
+ </select>
129
+
130
+ <label>
131
+ <input type="checkbox" id="agree" name="agree" />
132
+ I agree
133
+ </label>
134
+
135
+ <button type="submit" id="submit">Submit</button>
136
+ </form>
137
+ </body>
138
+ </html>
139
+ `);
140
+ });
141
+
142
+ test('browser_fill - should fill text input', async () => {
143
+ const tools = getAllTools(page);
144
+ const fillTool = tools.find(t => t.name === 'browser_fill');
145
+
146
+ const result = await fillTool!.invoke('{"selector": "#username", "value": "john"}');
147
+ expect(result).toContain('Filled');
148
+
149
+ const value = await page.$eval('#username', (el: any) => el.value);
150
+ expect(value).toBe('john');
151
+ });
152
+
153
+ test('browser_fill - should fill by label', async () => {
154
+ const tools = getAllTools(page);
155
+ const fillTool = tools.find(t => t.name === 'browser_fill');
156
+
157
+ const result = await fillTool!.invoke('{"selector": "Email", "value": "test@example.com"}');
158
+ expect(result).toContain('Filled');
159
+
160
+ const value = await page.$eval('#email', (el: any) => el.value);
161
+ expect(value).toBe('test@example.com');
162
+ });
163
+
164
+ test('browser_select_option - should select dropdown option', async () => {
165
+ const tools = getAllTools(page);
166
+ const selectTool = tools.find(t => t.name === 'browser_select_option');
167
+
168
+ const result = await selectTool!.invoke('{"selector": "#country", "value": "china"}');
169
+ expect(result).toContain('Selected');
170
+
171
+ const value = await page.$eval('#country', (el: any) => el.value);
172
+ expect(value).toBe('china');
173
+ });
174
+
175
+ test('browser_set_checked - should check checkbox', async () => {
176
+ const tools = getAllTools(page);
177
+ const checkTool = tools.find(t => t.name === 'browser_set_checked');
178
+
179
+ // 勾选
180
+ let result = await checkTool!.invoke('{"selector": "#agree", "checked": true}');
181
+ expect(result).toContain('Checked');
182
+
183
+ let checked = await page.$eval('#agree', (el: any) => el.checked);
184
+ expect(checked).toBe(true);
185
+
186
+ // 取消勾选
187
+ result = await checkTool!.invoke('{"selector": "#agree", "checked": false}');
188
+ expect(result).toContain('Unchecked');
189
+
190
+ checked = await page.$eval('#agree', (el: any) => el.checked);
191
+ expect(checked).toBe(false);
192
+ });
193
+ });
194
+
195
+ test.describe('Wait Tools', () => {
196
+ test('browser_wait_for_time - should wait for specified time', async () => {
197
+ const tools = getAllTools(page);
198
+ const waitTool = tools.find(t => t.name === 'browser_wait_for_time');
199
+
200
+ const startTime = Date.now();
201
+ const result = await waitTool!.invoke('1000'); // 等待 1 秒
202
+ const duration = Date.now() - startTime;
203
+
204
+ expect(result).toContain('Waited');
205
+ expect(duration).toBeGreaterThanOrEqual(1000);
206
+ expect(duration).toBeLessThan(1500); // 允许一些误差
207
+ });
208
+
209
+ test('browser_wait_for_selector - should wait for element', async () => {
210
+ const tools = getAllTools(page);
211
+ const waitTool = tools.find(t => t.name === 'browser_wait_for_selector');
212
+
213
+ // 延迟添加元素
214
+ setTimeout(async () => {
215
+ await page.evaluate(() => {
216
+ const btn = document.createElement('button');
217
+ btn.id = 'delayed-button';
218
+ btn.textContent = 'Click Me';
219
+ document.body.appendChild(btn);
220
+ });
221
+ }, 500);
222
+
223
+ const result = await waitTool!.invoke('#delayed-button');
224
+ expect(result).toContain('became visible');
225
+ });
226
+
227
+ test('browser_wait_for_selector - should support timeout parameter', async () => {
228
+ const tools = getAllTools(page);
229
+ const waitTool = tools.find(t => t.name === 'browser_wait_for_selector');
230
+
231
+ try {
232
+ await waitTool!.invoke('#nonexistent, timeout=1000');
233
+ expect.fail('Should have thrown an error');
234
+ } catch (error) {
235
+ expect((error as Error).message).toContain('No element found');
236
+ }
237
+ });
238
+
239
+ test('browser_wait_for_url - should wait for URL', async () => {
240
+ const tools = getAllTools(page);
241
+ const waitTool = tools.find(t => t.name === 'browser_wait_for_url');
242
+
243
+ page.goto('https://example.com');
244
+
245
+ // 延迟导航
246
+ setTimeout(async () => {
247
+ await page.goto('https://example.com/about');
248
+ }, 500);
249
+
250
+ const result = await waitTool!.invoke('**/about');
251
+ expect(result).toContain('URL matched');
252
+ });
253
+ });
254
+
255
+ test.describe('Verify Tools', () => {
256
+ test.beforeEach(async () => {
257
+ await page.goto('https://example.com');
258
+ });
259
+
260
+ test('browser_get_url - should get current URL', async () => {
261
+ const tools = getAllTools(page);
262
+ const urlTool = tools.find(t => t.name === 'browser_get_url');
263
+
264
+ const result = await urlTool!.invoke('');
265
+ expect(result).toContain('Current URL');
266
+ expect(result).toContain('https://example.com');
267
+ });
268
+
269
+ test('browser_get_content - should get page content', async () => {
270
+ const tools = getAllTools(page);
271
+ const contentTool = tools.find(t => t.name === 'browser_get_content');
272
+
273
+ const result = await contentTool!.invoke('');
274
+ expect(result).toContain('Page content');
275
+ expect(result.length).toBeGreaterThan(0);
276
+ });
277
+
278
+ test('browser_get_text - should get element text', async () => {
279
+ const tools = getAllTools(page);
280
+ const textTool = tools.find(t => t.name === 'browser_get_text');
281
+
282
+ const result = await textTool!.invoke('h1');
283
+ expect(result).toContain('Example Domain');
284
+ });
285
+
286
+ test('browser_screenshot - should take screenshot', async () => {
287
+ const tools = getAllTools(page);
288
+ const screenshotTool = tools.find(t => t.name === 'browser_screenshot');
289
+
290
+ const result = await screenshotTool!.invoke('');
291
+ expect(result).toContain('Screenshot saved');
292
+ expect(result).toContain('screenshot-');
293
+ expect(result).toContain('.png');
294
+ });
295
+
296
+ test('browser_verify_text - should verify text is visible', async () => {
297
+ const tools = getAllTools(page);
298
+ const verifyTool = tools.find(t => t.name === 'browser_verify_text');
299
+
300
+ const result = await verifyTool!.invoke('Example Domain');
301
+ expect(result).toContain('is visible');
302
+ });
303
+
304
+ test('browser_verify_text - should throw error for non-existent text', async () => {
305
+ const tools = getAllTools(page);
306
+ const verifyTool = tools.find(t => t.name === 'browser_verify_text');
307
+
308
+ try {
309
+ await verifyTool!.invoke('NonExistentText123456');
310
+ expect.fail('Should have thrown an error');
311
+ } catch (error) {
312
+ expect((error as Error).message).toContain('not found');
313
+ }
314
+ });
315
+
316
+ test('browser_verify_element - should verify element is visible', async () => {
317
+ const tools = getAllTools(page);
318
+ const verifyTool = tools.find(t => t.name === 'browser_verify_element');
319
+
320
+ const result = await verifyTool!.invoke('h1');
321
+ expect(result).toContain('is visible');
322
+ });
323
+
324
+ test('browser_verify_element - should throw error for non-existent element', async () => {
325
+ const tools = getAllTools(page);
326
+ const verifyTool = tools.find(t => t.name === 'browser_verify_element');
327
+
328
+ try {
329
+ await verifyTool!.invoke('#nonexistent-element-123456');
330
+ expect.fail('Should have thrown an error');
331
+ } catch (error) {
332
+ expect((error as Error).message).toContain('not found');
333
+ }
334
+ });
335
+ });
336
+
337
+ test.describe('Tool Integration Tests', () => {
338
+ test('should navigate and interact with form', async () => {
339
+ const tools = getAllTools(page);
340
+
341
+ // 创建测试页面
342
+ await page.setContent(`
343
+ <!DOCTYPE html>
344
+ <html>
345
+ <body>
346
+ <form id="login-form">
347
+ <input type="text" id="username" placeholder="Username" />
348
+ <input type="password" id="password" placeholder="Password" />
349
+ <button type="submit">Login</button>
350
+ </form>
351
+ </body>
352
+ </html>
353
+ `);
354
+
355
+ // 导航到页面(已经在本地,不需要导航)
356
+ const fillTool = tools.find(t => t.name === 'browser_fill');
357
+ const clickTool = tools.find(t => t.name === 'browser_click');
358
+
359
+ // 填写表单
360
+ await fillTool!.invoke('{"selector": "#username", "value": "testuser"}');
361
+ await fillTool!.invoke('{"selector": "#password", "value": "testpass"}');
362
+
363
+ // 点击按钮
364
+ await clickTool!.invoke('button[type="submit"]');
365
+
366
+ // 验证
367
+ const username = await page.$eval('#username', (el: any) => el.value);
368
+ const password = await page.$eval('#password', (el: any) => el.value);
369
+
370
+ expect(username).toBe('testuser');
371
+ expect(password).toBe('testpass');
372
+ });
373
+
374
+ test('should handle multiple selector attempts', async () => {
375
+ const tools = getAllTools(page);
376
+ const clickTool = tools.find(t => t.name === 'browser_click');
377
+
378
+ await page.setContent(`
379
+ <button id="submit-btn">Submit</button>
380
+ `);
381
+
382
+ // 提供多个选择器,包括不存在的
383
+ const result = await clickTool!.invoke('.nonexistent, #submit-btn, button');
384
+ expect(result).toContain('Clicked');
385
+ });
386
+
387
+ test('should wait and verify', async () => {
388
+ const tools = getAllTools(page);
389
+
390
+ const waitTool = tools.find(t => t.name === 'browser_wait_for_selector');
391
+ const verifyTool = tools.find(t => t.name === 'browser_verify_text');
392
+
393
+ // 延迟添加文本
394
+ setTimeout(async () => {
395
+ await page.evaluate(() => {
396
+ const div = document.createElement('div');
397
+ div.id = 'status';
398
+ div.textContent = 'Success!';
399
+ document.body.appendChild(div);
400
+ });
401
+ }, 500);
402
+
403
+ // 等待元素
404
+ await waitTool!.invoke('#status');
405
+
406
+ // 验证文本
407
+ const result = await verifyTool!.invoke('Success!');
408
+ expect(result).toContain('is visible');
409
+ });
410
+ });
411
+
412
+ test.describe('Error Handling', () => {
413
+ test('should handle invalid JSON in form tools', async () => {
414
+ const tools = getAllTools(page);
415
+ const fillTool = tools.find(t => t.name === 'browser_fill');
416
+
417
+ try {
418
+ await fillTool!.invoke('invalid json');
419
+ expect.fail('Should have thrown an error');
420
+ } catch (error) {
421
+ expect((error as Error).message).toContain('Failed to fill');
422
+ }
423
+ });
424
+
425
+ test('should handle non-existent elements', async () => {
426
+ const tools = getAllTools(page);
427
+ const clickTool = tools.find(t => t.name === 'browser_click');
428
+
429
+ try {
430
+ await clickTool!.invoke('#definitely-not-existing-element');
431
+ expect.fail('Should have thrown an error');
432
+ } catch (error) {
433
+ expect((error as Error).message).toContain('Failed to click');
434
+ }
435
+ });
436
+
437
+ test('should handle timeout in wait tool', async () => {
438
+ const tools = getAllTools(page);
439
+ const waitTool = tools.find(t => t.name === 'browser_wait_for_selector');
440
+
441
+ try {
442
+ await waitTool!.invoke('#nonexistent, timeout=500');
443
+ expect.fail('Should have thrown an error');
444
+ } catch (error) {
445
+ expect((error as Error).message).toContain('Failed to wait');
446
+ }
447
+ });
448
+ });
449
+
450
+ test.describe('Tool Discovery', () => {
451
+ test('should export all tools', () => {
452
+ const tools = getAllTools(page);
453
+
454
+ // 验证所有预期的工具都存在
455
+ const expectedTools = [
456
+ 'browser_navigate',
457
+ 'browser_navigate_back',
458
+ 'browser_navigate_forward',
459
+ 'browser_snapshot',
460
+ 'browser_click',
461
+ 'browser_hover',
462
+ 'browser_fill',
463
+ 'browser_select_option',
464
+ 'browser_set_checked',
465
+ 'browser_wait_for_time',
466
+ 'browser_wait_for_selector',
467
+ 'browser_wait_for_url',
468
+ 'browser_get_url',
469
+ 'browser_get_content',
470
+ 'browser_get_text',
471
+ 'browser_screenshot',
472
+ 'browser_verify_text',
473
+ 'browser_verify_element',
474
+ ];
475
+
476
+ const toolNames = tools.map(t => t.name);
477
+ expect(toolNames).toEqual(expect.arrayContaining(expectedTools));
478
+ });
479
+
480
+ test('all tools should have name and description', () => {
481
+ const tools = getAllTools(page);
482
+
483
+ for (const tool of tools) {
484
+ expect(tool.name).toBeDefined();
485
+ expect(tool.name).toBeTruthy();
486
+ expect(tool.description).toBeDefined();
487
+ expect(tool.description).toBeTruthy();
488
+ }
489
+ });
490
+ });
491
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "sourceMap": true,
9
+ "outDir": "./dist",
10
+ "rootDir": "./src",
11
+ "strict": true,
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "resolveJsonModule": true,
16
+ "moduleResolution": "node",
17
+ "allowSyntheticDefaultImports": true,
18
+ "types": ["node", "@playwright/test"]
19
+ },
20
+ "include": ["src/**/*"],
21
+ "exclude": ["node_modules", "dist", "tests"]
22
+ }