@allwright.dev/core 0.1.6 → 0.1.7

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 CHANGED
@@ -33,6 +33,20 @@ await page.click("a[target=_blank]");
33
33
  const newPage = await hook.wait();
34
34
  ```
35
35
 
36
+ ```ts
37
+ const hook = await page.registerHook(hooks.fileChooser);
38
+ await page.click("button.open-upload");
39
+ const chooser = await hook.wait();
40
+ await chooser.setFiles("fixtures/document.pdf");
41
+ ```
42
+
43
+ ```ts
44
+ const hook = await page.registerHook(hooks.download);
45
+ await page.click("a.download-report");
46
+ const download = await hook.wait();
47
+ await download.saveAs(`artifacts/${download.suggestedFilename}`);
48
+ ```
49
+
36
50
  Runnable examples live in [examples/web-basic.ts](./examples/web-basic.ts) and [examples/android-basic.ts](./examples/android-basic.ts).
37
51
 
38
52
  Shared config files are stack-agnostic and can live in `allwright.config.yaml` or `allwright.config.json`.
package/dist/bootstrap.js CHANGED
@@ -12,7 +12,7 @@ const ALLWRIGHT_HOME_ENV_VAR = "ALLWRIGHT_HOME";
12
12
  const ALLWRIGHT_REPOSITORY_ENV_VAR = "ALLWRIGHT_REPOSITORY";
13
13
  const ALLWRIGHT_VERSION_ENV_VAR = "ALLWRIGHT_VERSION";
14
14
  const DEFAULT_RELEASE_REPOSITORY = "allwright-dev/allwright";
15
- const DEFAULT_RELEASE_VERSION = "0.1.6";
15
+ const DEFAULT_RELEASE_VERSION = "0.1.7";
16
16
  const STARTUP_TIMEOUT_MS = 20_000;
17
17
  const PING_TIMEOUT_MS = 1_000;
18
18
  const PROTO_ROOT = fileURLToPath(new URL("../proto/", import.meta.url));
package/dist/index.d.ts CHANGED
@@ -3,13 +3,15 @@ import { findConfigFile, loadConfigFile, resolveConfig } from "./config.js";
3
3
  import { mobile } from "./mobile.js";
4
4
  import { PageImpl } from "./page.js";
5
5
  import { setServerAddr, shutdown } from "./runtime.js";
6
- import type { Browser, BrowserKind, BrowserType, LaunchOptions, Page, HookType, ResolveConfigOptions, ResolvedAllwrightConfig } from "./types.js";
6
+ import type { Browser, BrowserKind, BrowserType, LaunchOptions, Page, FileChooser, Download, HookType, ResolveConfigOptions, ResolvedAllwrightConfig } from "./types.js";
7
7
  export { findConfigFile, loadConfigFile, resolveConfig, setServerAddr, shutdown };
8
- export type { AccessibilitySnapshotOptions, AllwrightConfig, Browser, BrowserInfo, BrowserKind, BrowserType, Hook, HookType, ClickResult, CommandOptions, CountResult, ElementResult, FillResult, HighlightOptions, HighlightResult, LaunchOptions, MobileAndroidConnectOptions, MobileAndroidDevice, MobileAndroidLaunchOptions, MobileAndroidLocator, MobileAndroidApp, Locator, LocatorInfo, MobileSurfaceNamespace, NavigateResult, Page, PageInfo, PressOptions, PressResult, ResolveConfigOptions, ResolvedAllwrightConfig, RetryConfig, ScreenshotOptions, ScreenshotResult, TextResult, WaitForSelectorOptions, WaitForSelectorResult, } from "./types.js";
8
+ export type { AccessibilitySnapshotOptions, AllwrightConfig, Browser, BrowserInfo, BrowserKind, BrowserType, Hook, HookType, FileChooser, Download, ClickResult, CommandOptions, CountResult, ElementResult, FillResult, HighlightOptions, HighlightResult, LaunchOptions, MobileAndroidConnectOptions, MobileAndroidDevice, MobileAndroidLaunchOptions, MobileAndroidLocator, MobileAndroidApp, Locator, LocatorInfo, MobileSurfaceNamespace, NavigateResult, Page, PageInfo, PressOptions, PressResult, ResolveConfigOptions, ResolvedAllwrightConfig, RetryConfig, ScreenshotOptions, ScreenshotResult, TextResult, WaitForSelectorOptions, WaitForSelectorResult, } from "./types.js";
9
9
  export declare const chromium: BrowserType;
10
10
  export declare const firefox: BrowserType;
11
11
  export declare const hooks: {
12
12
  newPage: HookType<Page>;
13
+ fileChooser: HookType<FileChooser>;
14
+ download: HookType<Download>;
13
15
  };
14
16
  export { mobile };
15
17
  export type Tab = Page;
package/dist/index.js CHANGED
@@ -9,6 +9,8 @@ export const chromium = new BrowserTypeImpl("chromium");
9
9
  export const firefox = new BrowserTypeImpl("firefox");
10
10
  export const hooks = {
11
11
  newPage: Object.freeze({ name: "newPage" }),
12
+ fileChooser: Object.freeze({ name: "fileChooser" }),
13
+ download: Object.freeze({ name: "download" }),
12
14
  };
13
15
  export { mobile };
14
16
  export async function ping() {
package/dist/page.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { WebLocatorBuilders, type LocatorFilterOptions } from "./web-locators.js";
2
- import type { AccessibilitySnapshotOptions, ClickResult, CommandOptions, CountResult, ElementResult, FillResult, HighlightOptions, HighlightResult, Hook, HookType, Locator, NavigateResult, Page, PageInfo, PressOptions, PressResult, RuntimeClient, ScreenshotOptions, ScreenshotResult, TextResult, WaitForSelectorOptions, WaitForSelectorResult } from "./types.js";
2
+ import type { AccessibilitySnapshotOptions, ClickResult, CommandOptions, CountResult, ElementResult, FillResult, FileChooser, Download, HighlightOptions, HighlightResult, Hook, HookType, Locator, NavigateResult, Page, PageInfo, PressOptions, PressResult, RuntimeClient, ScreenshotOptions, ScreenshotResult, TextResult, WaitForSelectorOptions, WaitForSelectorResult } from "./types.js";
3
3
  export declare class PageImpl extends WebLocatorBuilders implements Page {
4
4
  #private;
5
5
  constructor(input: PageInfo & {
@@ -9,6 +9,8 @@ export declare class PageImpl extends WebLocatorBuilders implements Page {
9
9
  readonly sessionId: string;
10
10
  readonly browserSessionId: string;
11
11
  registerHook<T>(type: HookType<T>): Promise<Hook<T>>;
12
+ setFileChooserFiles(fileChooserId: string, files: string[], options?: CommandOptions): Promise<void>;
13
+ saveDownload(downloadId: string, path: string, options?: CommandOptions): Promise<void>;
12
14
  locator(selector: string, options?: LocatorFilterOptions): Locator;
13
15
  goto(url: string, options?: CommandOptions): Promise<NavigateResult>;
14
16
  click(selector: string, options?: CommandOptions): Promise<ClickResult>;
@@ -29,3 +31,19 @@ export declare class PageImpl extends WebLocatorBuilders implements Page {
29
31
  navigate(url: string, options?: CommandOptions): Promise<NavigateResult>;
30
32
  dispose(): void;
31
33
  }
34
+ export declare class FileChooserImpl implements FileChooser {
35
+ readonly page: PageImpl;
36
+ readonly id: string;
37
+ readonly multiple: boolean;
38
+ constructor(page: PageImpl, id: string, multiple: boolean);
39
+ isMultiple(): boolean;
40
+ setFiles(files: string | string[], options?: CommandOptions): Promise<void>;
41
+ }
42
+ export declare class DownloadImpl implements Download {
43
+ readonly page: PageImpl;
44
+ readonly id: string;
45
+ readonly url: string;
46
+ readonly suggestedFilename: string;
47
+ constructor(page: PageImpl, id: string, url: string, suggestedFilename: string);
48
+ saveAs(path: string, options?: CommandOptions): Promise<void>;
49
+ }
package/dist/page.js CHANGED
@@ -23,7 +23,7 @@ export class PageImpl extends WebLocatorBuilders {
23
23
  sessionId;
24
24
  browserSessionId;
25
25
  async registerHook(type) {
26
- if (type.name !== "newPage") {
26
+ if (type.name !== "newPage" && type.name !== "fileChooser" && type.name !== "download") {
27
27
  throw formatActionError("register hook", `unsupported hook type: ${type.name}`);
28
28
  }
29
29
  const handle = await this.#getHandle();
@@ -31,7 +31,11 @@ export class PageImpl extends WebLocatorBuilders {
31
31
  handle.stream.write({
32
32
  surfaceSessionId: this.browserSessionId,
33
33
  contextSessionId: this.sessionId,
34
- registerHook: { newPage: {} },
34
+ registerHook: type.name === "newPage"
35
+ ? { newPage: {} }
36
+ : type.name === "fileChooser"
37
+ ? { fileChooser: {} }
38
+ : { download: {} },
35
39
  });
36
40
  while (true) {
37
41
  const event = await handle.queue.next();
@@ -65,6 +69,12 @@ export class PageImpl extends WebLocatorBuilders {
65
69
  if (type.name === "newPage" && event.hookCompleted.newPage?.contextSessionId) {
66
70
  return this.#createPage(event.hookCompleted.newPage.contextSessionId);
67
71
  }
72
+ if (type.name === "fileChooser" && event.hookCompleted.fileChooser?.fileChooserId) {
73
+ return new FileChooserImpl(this, event.hookCompleted.fileChooser.fileChooserId, event.hookCompleted.fileChooser.isMultiple ?? false);
74
+ }
75
+ if (type.name === "download" && event.hookCompleted.download?.downloadId) {
76
+ return new DownloadImpl(this, event.hookCompleted.download.downloadId, event.hookCompleted.download.url ?? "", event.hookCompleted.download.suggestedFilename ?? "");
77
+ }
68
78
  throw formatActionError(`wait for ${type.name} hook`, "hook returned an invalid result");
69
79
  }
70
80
  if (event.error?.message) {
@@ -72,6 +82,50 @@ export class PageImpl extends WebLocatorBuilders {
72
82
  }
73
83
  }
74
84
  }
85
+ async setFileChooserFiles(fileChooserId, files, options = {}) {
86
+ const handle = await this.#getHandle();
87
+ this.#ensureOpen(handle);
88
+ handle.stream.write({
89
+ surfaceSessionId: this.browserSessionId,
90
+ contextSessionId: this.sessionId,
91
+ setFileChooserFiles: {
92
+ fileChooserId,
93
+ files,
94
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
95
+ },
96
+ });
97
+ while (true) {
98
+ const event = await handle.queue.next();
99
+ if (event.fileChooserFilesSet?.fileChooserId === fileChooserId) {
100
+ return;
101
+ }
102
+ if (event.error?.message) {
103
+ throw formatActionError("set file chooser files", event.error.message);
104
+ }
105
+ }
106
+ }
107
+ async saveDownload(downloadId, path, options = {}) {
108
+ const handle = await this.#getHandle();
109
+ this.#ensureOpen(handle);
110
+ handle.stream.write({
111
+ surfaceSessionId: this.browserSessionId,
112
+ contextSessionId: this.sessionId,
113
+ saveDownload: {
114
+ downloadId,
115
+ path,
116
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
117
+ },
118
+ });
119
+ while (true) {
120
+ const event = await handle.queue.next();
121
+ if (event.downloadSaved?.downloadId === downloadId) {
122
+ return;
123
+ }
124
+ if (event.error?.message) {
125
+ throw formatActionError("save download", event.error.message);
126
+ }
127
+ }
128
+ }
75
129
  locator(selector, options) {
76
130
  const result = new LocatorImpl({ page: this, selector: normalizeSelectorForTransport(selector) });
77
131
  return options ? result.filter(options) : result;
@@ -566,3 +620,38 @@ export class PageImpl extends WebLocatorBuilders {
566
620
  });
567
621
  }
568
622
  }
623
+ export class FileChooserImpl {
624
+ page;
625
+ id;
626
+ multiple;
627
+ constructor(page, id, multiple) {
628
+ this.page = page;
629
+ this.id = id;
630
+ this.multiple = multiple;
631
+ }
632
+ isMultiple() {
633
+ return this.multiple;
634
+ }
635
+ async setFiles(files, options = {}) {
636
+ const paths = typeof files === "string" ? [files] : files;
637
+ if (!this.multiple && paths.length > 1) {
638
+ throw formatActionError("set file chooser files", "file chooser does not accept multiple files");
639
+ }
640
+ await this.page.setFileChooserFiles(this.id, paths, options);
641
+ }
642
+ }
643
+ export class DownloadImpl {
644
+ page;
645
+ id;
646
+ url;
647
+ suggestedFilename;
648
+ constructor(page, id, url, suggestedFilename) {
649
+ this.page = page;
650
+ this.id = id;
651
+ this.url = url;
652
+ this.suggestedFilename = suggestedFilename;
653
+ }
654
+ async saveAs(path, options = {}) {
655
+ await this.page.saveDownload(this.id, path, options);
656
+ }
657
+ }
package/dist/types.d.ts CHANGED
@@ -108,6 +108,19 @@ export interface Hook<T> {
108
108
  readonly type: HookType<T>;
109
109
  wait(options?: CommandOptions): Promise<T>;
110
110
  }
111
+ export interface FileChooser {
112
+ readonly id: string;
113
+ readonly page: Page;
114
+ isMultiple(): boolean;
115
+ setFiles(files: string | string[], options?: CommandOptions): Promise<void>;
116
+ }
117
+ export interface Download {
118
+ readonly id: string;
119
+ readonly page: Page;
120
+ readonly url: string;
121
+ readonly suggestedFilename: string;
122
+ saveAs(path: string, options?: CommandOptions): Promise<void>;
123
+ }
111
124
  export interface Browser extends BrowserInfo {
112
125
  page(): Page;
113
126
  initialPage(): Page;
@@ -387,6 +400,27 @@ export interface ContextSessionEvent {
387
400
  contextSessionId?: string;
388
401
  note?: string;
389
402
  };
403
+ fileChooser?: {
404
+ fileChooserId?: string;
405
+ isMultiple?: boolean;
406
+ note?: string;
407
+ };
408
+ download?: {
409
+ downloadId?: string;
410
+ url?: string;
411
+ suggestedFilename?: string;
412
+ note?: string;
413
+ };
414
+ };
415
+ fileChooserFilesSet?: {
416
+ fileChooserId?: string;
417
+ files?: string[];
418
+ note?: string;
419
+ };
420
+ downloadSaved?: {
421
+ downloadId?: string;
422
+ path?: string;
423
+ note?: string;
390
424
  };
391
425
  navigated?: {
392
426
  url?: string;
@@ -481,6 +515,30 @@ export interface RegisterHookRequest {
481
515
  contextSessionId: string;
482
516
  registerHook: {
483
517
  newPage?: Record<string, never>;
518
+ fileChooser?: Record<string, never>;
519
+ download?: Record<string, never>;
520
+ };
521
+ }
522
+ export interface SaveDownloadRequest {
523
+ surfaceSessionId: string;
524
+ contextSessionId: string;
525
+ saveDownload: {
526
+ downloadId: string;
527
+ path: string;
528
+ retryOptions?: {
529
+ timeoutMs?: number;
530
+ };
531
+ };
532
+ }
533
+ export interface SetFileChooserFilesRequest {
534
+ surfaceSessionId: string;
535
+ contextSessionId: string;
536
+ setFileChooserFiles: {
537
+ fileChooserId: string;
538
+ files: string[];
539
+ retryOptions?: {
540
+ timeoutMs?: number;
541
+ };
484
542
  };
485
543
  }
486
544
  export interface WaitForHookRequest {
@@ -672,7 +730,7 @@ export interface CloseContextRequest {
672
730
  close: Record<string, never>;
673
731
  }
674
732
  export type SurfaceSessionRequest = LaunchBrowserRequest | LaunchChromeRequest | OpenContextRequest | ConnectMobileRequest | LaunchAppRequest | SurfacePingRequest | CloseSurfaceRequest;
675
- export type ContextSessionRequest = ContextPingRequest | RegisterHookRequest | WaitForHookRequest | NavigateRequest | ClickRequest | CountRequest | HighlightRequest | FocusRequest | FillRequest | HoverRequest | PressRequest | TextContentRequest | InnerTextRequest | WaitForSelectorRequest | AccessibilitySnapshotRequest | ScreenshotRequest | CloseContextRequest;
733
+ export type ContextSessionRequest = ContextPingRequest | RegisterHookRequest | WaitForHookRequest | SetFileChooserFilesRequest | SaveDownloadRequest | NavigateRequest | ClickRequest | CountRequest | HighlightRequest | FocusRequest | FillRequest | HoverRequest | PressRequest | TextContentRequest | InnerTextRequest | WaitForSelectorRequest | AccessibilitySnapshotRequest | ScreenshotRequest | CloseContextRequest;
676
734
  export type SurfaceSessionStream = grpc.ClientDuplexStream<SurfaceSessionRequest, SurfaceSessionEvent>;
677
735
  export type ContextSessionStream = grpc.ClientDuplexStream<ContextSessionRequest, ContextSessionEvent>;
678
736
  export interface EngineServiceClientShape {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allwright.dev/core",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "High-level TypeScript client for the allwright automation engine.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,6 +30,8 @@ message ContextSessionCommand {
30
30
  AccessibilitySnapshotCommand accessibility_snapshot = 17;
31
31
  RegisterHookCommand register_hook = 18;
32
32
  WaitForHookCommand wait_for_hook = 19;
33
+ SetFileChooserFilesCommand set_file_chooser_files = 20;
34
+ SaveDownloadCommand save_download = 21;
33
35
  }
34
36
  }
35
37
 
@@ -63,6 +65,8 @@ message ContextSessionEvent {
63
65
  AccessibilitySnapshotCapturedEvent accessibility_snapshot_captured = 19;
64
66
  HookRegisteredEvent hook_registered = 20;
65
67
  HookCompletedEvent hook_completed = 21;
68
+ FileChooserFilesSetEvent file_chooser_files_set = 22;
69
+ DownloadSavedEvent download_saved = 23;
66
70
  }
67
71
  }
68
72
 
@@ -85,6 +89,8 @@ message ContextSessionErrorEvent {
85
89
  message RegisterHookCommand {
86
90
  oneof hook {
87
91
  RegisterNewPageHook new_page = 1;
92
+ RegisterFileChooserHook file_chooser = 2;
93
+ RegisterDownloadHook download = 3;
88
94
  }
89
95
  }
90
96
 
@@ -102,6 +108,8 @@ message HookCompletedEvent {
102
108
 
103
109
  oneof result {
104
110
  NewPageHookResult new_page = 2;
111
+ FileChooserHookResult file_chooser = 3;
112
+ DownloadHookResult download = 4;
105
113
  }
106
114
  }
107
115
 
@@ -46,3 +46,44 @@ message NewPageHookResult {
46
46
  string context_session_id = 1;
47
47
  string note = 2;
48
48
  }
49
+
50
+ message RegisterFileChooserHook {}
51
+
52
+ message FileChooserHookResult {
53
+ string file_chooser_id = 1;
54
+ bool is_multiple = 2;
55
+ string note = 3;
56
+ }
57
+
58
+ message SetFileChooserFilesCommand {
59
+ string file_chooser_id = 1;
60
+ repeated string files = 2;
61
+ optional CommandRetryOptions retry_options = 3;
62
+ }
63
+
64
+ message FileChooserFilesSetEvent {
65
+ string file_chooser_id = 1;
66
+ repeated string files = 2;
67
+ string note = 3;
68
+ }
69
+
70
+ message RegisterDownloadHook {}
71
+
72
+ message DownloadHookResult {
73
+ string download_id = 1;
74
+ string url = 2;
75
+ string suggested_filename = 3;
76
+ string note = 4;
77
+ }
78
+
79
+ message SaveDownloadCommand {
80
+ string download_id = 1;
81
+ string path = 2;
82
+ optional CommandRetryOptions retry_options = 3;
83
+ }
84
+
85
+ message DownloadSavedEvent {
86
+ string download_id = 1;
87
+ string path = 2;
88
+ string note = 3;
89
+ }