@percy/core 1.32.0-beta.0 → 1.32.0-beta.1
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/dist/api.js +690 -16
- package/dist/maestro-hierarchy.js +1769 -0
- package/dist/percy.js +18 -0
- package/dist/proto/README.md +47 -0
- package/dist/proto/maestro_android.proto +116 -0
- package/package.json +13 -9
package/dist/percy.js
CHANGED
|
@@ -20,6 +20,7 @@ import { gatherSnapshots, createSnapshotsQueue, validateSnapshotOptions } from '
|
|
|
20
20
|
import { discoverSnapshotResources, createDiscoveryQueue, RESOURCE_CACHE_KEY, CACHE_STATS_KEY, DISK_SPILL_KEY } from './discovery.js';
|
|
21
21
|
import Monitoring from '@percy/monitoring';
|
|
22
22
|
import { WaitForJob } from './wait-for-job.js';
|
|
23
|
+
import { closeGrpcClientCache } from './maestro-hierarchy.js';
|
|
23
24
|
const MAX_SUGGESTION_CALLS = 10;
|
|
24
25
|
|
|
25
26
|
// If no activity is done for 5 mins, we will stop monitoring
|
|
@@ -121,6 +122,16 @@ export class Percy {
|
|
|
121
122
|
this.monitoringCheckLastExecutedAt = null;
|
|
122
123
|
this.sdkInfoDisplayed = false;
|
|
123
124
|
|
|
125
|
+
// Per-Percy gRPC client cache for the Android view-hierarchy resolver
|
|
126
|
+
// (D9 in 2026-05-07-002 plan). Owns transport state — channels hold open
|
|
127
|
+
// sockets, must be closed in stop(). Module-scoped state would leak
|
|
128
|
+
// between concurrent Percy instances and cause cross-instance shutdown
|
|
129
|
+
// races. The drift envelope (maestroHierarchyDrift in maestro-hierarchy.js)
|
|
130
|
+
// stays module-scoped because drift is observability state — surfaced
|
|
131
|
+
// process-wide on /percy/healthcheck. Two scopes, two reasons.
|
|
132
|
+
this.grpcClientCache = new Map();
|
|
133
|
+
this.grpcClientCache.shutdownInProgress = false;
|
|
134
|
+
|
|
124
135
|
// Domain validation state for auto domain allow-listing
|
|
125
136
|
this.domainValidation = {
|
|
126
137
|
autoConfiguredHosts: new Set(),
|
|
@@ -429,6 +440,13 @@ export class Percy {
|
|
|
429
440
|
await _classPrivateFieldGet(_discovery, this).end();
|
|
430
441
|
await _classPrivateFieldGet(_snapshots, this).end();
|
|
431
442
|
|
|
443
|
+
// Close gRPC channels for the Android view-hierarchy resolver. Set the
|
|
444
|
+
// shutdown flag first so any in-flight runAndroidGrpcDump() that hits
|
|
445
|
+
// CANCELLED returns {kind:'unavailable', reason:'shutdown'} instead of
|
|
446
|
+
// triggering the fallback chain on a tearing-down process (R-7).
|
|
447
|
+
this.grpcClientCache.shutdownInProgress = true;
|
|
448
|
+
closeGrpcClientCache(this.grpcClientCache);
|
|
449
|
+
|
|
432
450
|
// mark instance as stopped
|
|
433
451
|
this.readyState = 3;
|
|
434
452
|
} catch (err) {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Vendored protobuf — `maestro_android.proto`
|
|
2
|
+
|
|
3
|
+
Direct copy of the protobuf schema served by `dev.mobile.maestro` on Android
|
|
4
|
+
devices, used by `@percy/core`'s element-region resolver to call
|
|
5
|
+
`maestro_android.MaestroDriver/viewHierarchy` directly over gRPC instead of
|
|
6
|
+
spawning the full `maestro` CLI (~9s JVM cold start per call → <100ms direct
|
|
7
|
+
gRPC call).
|
|
8
|
+
|
|
9
|
+
## Source
|
|
10
|
+
|
|
11
|
+
- **Upstream file:** `maestro-proto/src/main/proto/maestro_android.proto`
|
|
12
|
+
- **Upstream repo:** [`mobile-dev-inc/Maestro`](https://github.com/mobile-dev-inc/Maestro)
|
|
13
|
+
- **Commit SHA at copy time:** `bc8bde1b5cb7f2d4076047c0a9db094ece47512f` (2025-05-26)
|
|
14
|
+
- **Closest CLI release:** `cli-2.5.1`
|
|
15
|
+
- **Copy date:** 2026-04-29
|
|
16
|
+
|
|
17
|
+
## What we use
|
|
18
|
+
|
|
19
|
+
Only `MaestroDriver/viewHierarchy(ViewHierarchyRequest) returns (ViewHierarchyResponse)`
|
|
20
|
+
and the `string hierarchy = 1` field on the response. The rest of the proto
|
|
21
|
+
is included unchanged so future updates can be a clean upstream re-copy
|
|
22
|
+
without surgical edits.
|
|
23
|
+
|
|
24
|
+
## Drift policy
|
|
25
|
+
|
|
26
|
+
- The proto **must** be re-vendored from upstream whenever the Maestro CLI
|
|
27
|
+
version deployed on BrowserStack hosts is bumped past the version recorded
|
|
28
|
+
above. PRs that update this file must paste the upstream SHA and CLI tag.
|
|
29
|
+
- The runtime parser (`@grpc/proto-loader`) silently drops unknown fields.
|
|
30
|
+
If `viewHierarchy`'s response field is renumbered, retyped, or replaced,
|
|
31
|
+
decode errors surface as `dump-error (grpc-decode)` and a
|
|
32
|
+
`maestroHierarchyDrift` flag appears on the `/percy/healthcheck` response.
|
|
33
|
+
See `docs/solutions/integration-issues/percy-labels-cli-schema-rejection-2026-04-23.md`
|
|
34
|
+
for context on why we monitor schema drift loudly.
|
|
35
|
+
|
|
36
|
+
## How to refresh
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
curl -fsSL "https://raw.githubusercontent.com/mobile-dev-inc/Maestro/main/maestro-proto/src/main/proto/maestro_android.proto" \
|
|
40
|
+
-o packages/core/src/proto/maestro_android.proto
|
|
41
|
+
# Update the SHA + CLI tag above; PR must show the diff and the new pin.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The file is loaded at module init via `@grpc/proto-loader`'s `loadSync` from
|
|
45
|
+
`packages/core/src/maestro-hierarchy.js`. Babel CLI's `copyFiles: true`
|
|
46
|
+
(scripts/build.js:26) preserves the relative layout so it lands at
|
|
47
|
+
`dist/proto/maestro_android.proto` after `yarn build`.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package maestro_android;
|
|
4
|
+
|
|
5
|
+
service MaestroDriver {
|
|
6
|
+
|
|
7
|
+
rpc deviceInfo(DeviceInfoRequest) returns (DeviceInfo) {}
|
|
8
|
+
|
|
9
|
+
rpc viewHierarchy(ViewHierarchyRequest) returns (ViewHierarchyResponse) {}
|
|
10
|
+
|
|
11
|
+
rpc screenshot(ScreenshotRequest) returns (ScreenshotResponse) {}
|
|
12
|
+
|
|
13
|
+
rpc tap(TapRequest) returns (TapResponse) {}
|
|
14
|
+
|
|
15
|
+
rpc inputText(InputTextRequest) returns (InputTextResponse) {}
|
|
16
|
+
|
|
17
|
+
rpc eraseAllText(EraseAllTextRequest) returns (EraseAllTextResponse) {}
|
|
18
|
+
|
|
19
|
+
rpc setLocation(SetLocationRequest) returns (SetLocationResponse) {}
|
|
20
|
+
|
|
21
|
+
rpc isWindowUpdating(CheckWindowUpdatingRequest) returns (CheckWindowUpdatingResponse) {}
|
|
22
|
+
|
|
23
|
+
rpc launchApp(LaunchAppRequest) returns (LaunchAppResponse) {}
|
|
24
|
+
|
|
25
|
+
rpc addMedia(stream AddMediaRequest) returns (AddMediaResponse) {}
|
|
26
|
+
|
|
27
|
+
rpc enableMockLocationProviders(EmptyRequest) returns (EmptyResponse) {}
|
|
28
|
+
|
|
29
|
+
rpc disableLocationUpdates(EmptyRequest) returns (EmptyResponse) {}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
message EmptyRequest {}
|
|
33
|
+
message EmptyResponse {}
|
|
34
|
+
|
|
35
|
+
message LaunchAppRequest {
|
|
36
|
+
|
|
37
|
+
string packageName = 1;
|
|
38
|
+
repeated ArgumentValue arguments = 2;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
message ArgumentValue {
|
|
42
|
+
string key = 1;
|
|
43
|
+
string value = 2;
|
|
44
|
+
string type = 3;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
message LaunchAppResponse {}
|
|
48
|
+
|
|
49
|
+
// Device info
|
|
50
|
+
message DeviceInfoRequest {}
|
|
51
|
+
|
|
52
|
+
message DeviceInfo {
|
|
53
|
+
uint32 widthPixels = 1;
|
|
54
|
+
uint32 heightPixels = 2;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
message ScreenshotRequest {}
|
|
58
|
+
|
|
59
|
+
message ScreenshotResponse {
|
|
60
|
+
bytes bytes = 1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// View hierarchy
|
|
64
|
+
message ViewHierarchyRequest {}
|
|
65
|
+
|
|
66
|
+
message ViewHierarchyResponse {
|
|
67
|
+
string hierarchy = 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Interactions
|
|
71
|
+
|
|
72
|
+
message TapRequest {
|
|
73
|
+
uint32 x = 1;
|
|
74
|
+
uint32 y = 2;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
message TapResponse {}
|
|
78
|
+
|
|
79
|
+
message InputTextRequest {
|
|
80
|
+
string text = 1;
|
|
81
|
+
}
|
|
82
|
+
message InputTextResponse {}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
message EraseAllTextRequest {
|
|
86
|
+
uint32 charactersToErase = 1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
message EraseAllTextResponse {}
|
|
90
|
+
|
|
91
|
+
message SetLocationRequest {
|
|
92
|
+
double latitude = 1;
|
|
93
|
+
double longitude = 2;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
message SetLocationResponse {}
|
|
97
|
+
|
|
98
|
+
message CheckWindowUpdatingRequest {
|
|
99
|
+
string appId = 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
message CheckWindowUpdatingResponse {
|
|
103
|
+
bool isWindowUpdating = 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
message AddMediaRequest {
|
|
107
|
+
Payload payload = 1;
|
|
108
|
+
string media_name = 2;
|
|
109
|
+
string media_ext = 3;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
message AddMediaResponse { }
|
|
113
|
+
|
|
114
|
+
message Payload {
|
|
115
|
+
bytes data = 1;
|
|
116
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/core",
|
|
3
|
-
"version": "1.32.0-beta.
|
|
3
|
+
"version": "1.32.0-beta.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -44,16 +44,20 @@
|
|
|
44
44
|
"test:types": "tsd"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@
|
|
48
|
-
"@
|
|
49
|
-
"@percy/
|
|
50
|
-
"@percy/
|
|
51
|
-
"@percy/
|
|
52
|
-
"@percy/
|
|
47
|
+
"@grpc/grpc-js": "^1.14.3",
|
|
48
|
+
"@grpc/proto-loader": "^0.8.0",
|
|
49
|
+
"@percy/client": "1.32.0-beta.1",
|
|
50
|
+
"@percy/config": "1.32.0-beta.1",
|
|
51
|
+
"@percy/dom": "1.32.0-beta.1",
|
|
52
|
+
"@percy/logger": "1.32.0-beta.1",
|
|
53
|
+
"@percy/monitoring": "1.32.0-beta.1",
|
|
54
|
+
"@percy/webdriver-utils": "1.32.0-beta.1",
|
|
55
|
+
"busboy": "^1.6.0",
|
|
53
56
|
"content-disposition": "^0.5.4",
|
|
54
57
|
"cross-spawn": "^7.0.3",
|
|
55
58
|
"extract-zip": "^2.0.1",
|
|
56
59
|
"fast-glob": "^3.2.11",
|
|
60
|
+
"fast-xml-parser": "^4.4.1",
|
|
57
61
|
"micromatch": "^4.0.8",
|
|
58
62
|
"mime-types": "^2.1.34",
|
|
59
63
|
"pako": "^2.1.0",
|
|
@@ -63,7 +67,7 @@
|
|
|
63
67
|
"yaml": "^2.4.1"
|
|
64
68
|
},
|
|
65
69
|
"optionalDependencies": {
|
|
66
|
-
"@percy/cli-doctor": "1.32.0-beta.
|
|
70
|
+
"@percy/cli-doctor": "1.32.0-beta.1"
|
|
67
71
|
},
|
|
68
|
-
"gitHead": "
|
|
72
|
+
"gitHead": "899783cc84e5fd1b68c701c251cd8cabcbe35fd1"
|
|
69
73
|
}
|