browser-pilot 0.0.16 → 0.0.17

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.
@@ -0,0 +1,91 @@
1
+ // src/browser/upload.ts
2
+ async function uploadFiles(page, config) {
3
+ const { selector, files, timeout = 1e4 } = config;
4
+ const fileNames = files.map((f) => f.split("/").pop() ?? f);
5
+ try {
6
+ const selectors = Array.isArray(selector) ? selector : [selector];
7
+ let nodeId;
8
+ for (const sel of selectors) {
9
+ try {
10
+ const found = await page.waitFor(sel, {
11
+ timeout: Math.min(timeout, 5e3),
12
+ optional: true,
13
+ state: "attached"
14
+ });
15
+ if (found) {
16
+ const result = await page.evaluate(`(() => {
17
+ const el = document.querySelector(${JSON.stringify(sel)});
18
+ if (!el) return null;
19
+ return el.tagName.toLowerCase() === 'input' && el.type === 'file' ? 'file-input' : 'not-file-input';
20
+ })()`);
21
+ if (result === "file-input") {
22
+ const doc = await page.cdpClient.send("DOM.getDocument");
23
+ const queryResult = await page.cdpClient.send("DOM.querySelector", {
24
+ nodeId: doc.root.nodeId,
25
+ selector: sel
26
+ });
27
+ nodeId = queryResult.nodeId;
28
+ break;
29
+ }
30
+ }
31
+ } catch {
32
+ }
33
+ }
34
+ if (!nodeId) {
35
+ return {
36
+ accepted: false,
37
+ fileCount: 0,
38
+ fileNames,
39
+ error: "No file input element found"
40
+ };
41
+ }
42
+ await page.cdpClient.send("DOM.setFileInputFiles", {
43
+ files,
44
+ nodeId
45
+ });
46
+ await new Promise((resolve) => setTimeout(resolve, 300));
47
+ let validationError;
48
+ try {
49
+ const errorText = await page.evaluate(`(() => {
50
+ const errorSelectors = ['.error', '.validation-error', '[class*="error"]', '[role="alert"]'];
51
+ for (const sel of errorSelectors) {
52
+ const el = document.querySelector(sel);
53
+ if (el && el.offsetParent !== null && el.textContent.trim()) {
54
+ return el.textContent.trim();
55
+ }
56
+ }
57
+ return null;
58
+ })()`);
59
+ if (errorText) validationError = String(errorText);
60
+ } catch {
61
+ }
62
+ let visibleInUI;
63
+ try {
64
+ const visible = await page.evaluate(`(() => {
65
+ const text = document.body.innerText;
66
+ const fileNames = ${JSON.stringify(fileNames)};
67
+ return fileNames.some(name => text.includes(name));
68
+ })()`);
69
+ visibleInUI = visible === true;
70
+ } catch {
71
+ }
72
+ return {
73
+ accepted: true,
74
+ fileCount: files.length,
75
+ fileNames,
76
+ visibleInUI,
77
+ validationError
78
+ };
79
+ } catch (error) {
80
+ return {
81
+ accepted: false,
82
+ fileCount: 0,
83
+ fileNames,
84
+ error: error instanceof Error ? error.message : String(error)
85
+ };
86
+ }
87
+ }
88
+
89
+ export {
90
+ uploadFiles
91
+ };