@applitools/eyes-results 0.2.0 → 0.2.2
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/CHANGELOG.md +14 -0
- package/dist/api/diff-fetch.js +1 -1
- package/dist/api/polling.js +3 -0
- package/dist/api/url-parsing.js +35 -7
- package/dist/lib/env.js +6 -8
- package/package.json +1 -1
- package/types/api/url-parsing.d.ts +8 -0
- package/types/lib/env.d.ts +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.2](https://github.com/Applitools-Dev/sdk/compare/js/eyes-results@0.2.1...js/eyes-results@0.2.2) (2026-08-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* discover the project .env so the family read/write keys resolve | AD-15678 ([#4131](https://github.com/Applitools-Dev/sdk/issues/4131)) ([316f39f](https://github.com/Applitools-Dev/sdk/commit/316f39fd15fd516b3d266b8aa8deccddfabd0921))
|
|
9
|
+
|
|
10
|
+
## [0.2.1](https://github.com/Applitools-Dev/sdk/compare/js/eyes-results@0.2.0...js/eyes-results@0.2.1) (2026-08-24)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* enhance url support ([#4130](https://github.com/Applitools-Dev/sdk/issues/4130)) ([4dfc0a2](https://github.com/Applitools-Dev/sdk/commit/4dfc0a2553a910278596456f1c764fae72ab2c84))
|
|
16
|
+
|
|
3
17
|
## [0.2.0](https://github.com/Applitools-Dev/sdk/compare/js/eyes-results@0.1.0...js/eyes-results@0.2.0) (2026-08-23)
|
|
4
18
|
|
|
5
19
|
|
package/dist/api/diff-fetch.js
CHANGED
|
@@ -9,5 +9,5 @@ export async function fetchStepDiff(options, fetchImpl = reqTransport) {
|
|
|
9
9
|
const { baseUrl, batchId, sessionId, stepIndex, accountId, keys } = options;
|
|
10
10
|
const url = `${baseUrl}/api/sessions/batches/${batchId}/${sessionId}/${stepIndex}/diff?accountId=${accountId}&apiKey=${encodeURIComponent(keys.getReadKey())}`;
|
|
11
11
|
const data = await getJson(url, fetchImpl);
|
|
12
|
-
return { image1: data
|
|
12
|
+
return { image1: data?.image1 ?? [], image2: data?.image2 ?? [] };
|
|
13
13
|
}
|
package/dist/api/polling.js
CHANGED
|
@@ -31,6 +31,9 @@ export async function getJson(url, fetchImpl = reqTransport) {
|
|
|
31
31
|
const text = await response.text().catch(() => '');
|
|
32
32
|
throw new Error(`Request failed: ${response.status} ${response.statusText}${text ? ` — ${text}` : ''}`);
|
|
33
33
|
}
|
|
34
|
+
// 204 carries no body; the step-diff endpoint answers one for a step with no diff data.
|
|
35
|
+
if (response.status === 204)
|
|
36
|
+
return undefined;
|
|
34
37
|
return response.json();
|
|
35
38
|
}
|
|
36
39
|
/**
|
package/dist/api/url-parsing.js
CHANGED
|
@@ -1,21 +1,49 @@
|
|
|
1
|
+
// Dashboard route tokens under `/app/`. `testResults` is the canonical route the backend puts in
|
|
2
|
+
// reports, emails, and CI integrations; `batches` is what the Eyes server itself returns as a
|
|
3
|
+
// run's session URL — so it is the form a test run prints to the console and the form a user
|
|
4
|
+
// pastes first; `baselines` names a baseline revision, not a batch/session pair.
|
|
5
|
+
export const APP_ROUTES = { testResults: 'test-results', batches: 'batches', baselines: 'baselines' };
|
|
6
|
+
const escapeForRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
7
|
+
// Regex source for the `/app/<route>/` prefix of any batch-addressing dashboard URL.
|
|
8
|
+
// If a future route is a prefix of another, order longest-first (alternation is leftmost-match).
|
|
9
|
+
export const BATCH_ROUTE_PREFIX_SOURCE = `\\/app\\/(?:${[APP_ROUTES.testResults, APP_ROUTES.batches]
|
|
10
|
+
.map(escapeForRegExp)
|
|
11
|
+
.join('|')})\\/`;
|
|
12
|
+
// Everything after the two ids is a dashboard view (`/steps/{N}`, `/steps/{N}/edit`) that names
|
|
13
|
+
// no separate entity, so it is matched only to be discarded.
|
|
14
|
+
const BATCH_PATH = new RegExp(`${BATCH_ROUTE_PREFIX_SOURCE}([^/?#]+)(?:\\/([^/?#]+))?`);
|
|
15
|
+
// Route segments the dashboard puts directly after a batch id. Excluded by name because they sit
|
|
16
|
+
// in the position a session id would occupy, and a `steps` mistaken for a session id reaches the
|
|
17
|
+
// API as a real lookup.
|
|
18
|
+
const ROUTE_SEGMENTS = new Set(['steps', 'edit']);
|
|
19
|
+
// A baseline URL carries a baseline id and revision, not a batch/session pair, so no amount of
|
|
20
|
+
// permissiveness makes it parseable here — it needs its own message or the caller retries blind.
|
|
21
|
+
const BASELINE_PATH = new RegExp(`\\/app\\/${escapeForRegExp(APP_ROUTES.baselines)}\\/`);
|
|
22
|
+
const ACCEPTED_FORMS = `Accepted: https://<host>/app/${APP_ROUTES.testResults}/{batchId}[/{sessionId}[/steps/{N}]]?accountId=…, ` +
|
|
23
|
+
`or the https://<host>/app/${APP_ROUTES.batches}/{batchId}/{sessionId}?accountId=… form an SDK run prints.`;
|
|
1
24
|
/**
|
|
2
25
|
* Parses an Applitools dashboard URL (session or batch) into its components.
|
|
26
|
+
* Accepts any of the `APP_ROUTES` batch-addressing routes; rejections name the
|
|
27
|
+
* accepted forms so the caller can correct the URL without guessing.
|
|
3
28
|
* Does not require a sessionId to be present — use parseSessionUrl/parseBatchUrl
|
|
4
29
|
* for callers that need to enforce one shape or the other.
|
|
5
30
|
*/
|
|
6
31
|
export function parseApplitoolsUrl(url) {
|
|
7
32
|
const urlMatch = url.match(/^(https?:\/\/[^/]+)/);
|
|
8
33
|
if (!urlMatch)
|
|
9
|
-
throw new Error(`Invalid URL format: "${url}"`);
|
|
10
|
-
|
|
34
|
+
throw new Error(`Invalid URL format: "${url}". ${ACCEPTED_FORMS}`);
|
|
35
|
+
if (BASELINE_PATH.test(url)) {
|
|
36
|
+
throw new Error(`"${url}" is a baseline URL, which names a baseline revision rather than a batch or session. ${ACCEPTED_FORMS}`);
|
|
37
|
+
}
|
|
38
|
+
const batchMatch = url.match(BATCH_PATH);
|
|
11
39
|
if (!batchMatch)
|
|
12
|
-
throw new Error(`Invalid URL format: "${url}"`);
|
|
13
|
-
const accountIdMatch = url.match(/accountId=([
|
|
40
|
+
throw new Error(`Invalid URL format: "${url}". ${ACCEPTED_FORMS}`);
|
|
41
|
+
const accountIdMatch = url.match(/accountId=([^&#]+)/);
|
|
14
42
|
if (!accountIdMatch)
|
|
15
43
|
throw new Error(`Missing accountId in URL: "${url}"`);
|
|
16
|
-
const
|
|
17
|
-
const sessionId =
|
|
18
|
-
return { baseUrl: urlMatch[1], batchId
|
|
44
|
+
const [, batchId, segmentAfterBatch] = batchMatch;
|
|
45
|
+
const sessionId = segmentAfterBatch && !ROUTE_SEGMENTS.has(segmentAfterBatch) ? segmentAfterBatch : null;
|
|
46
|
+
return { baseUrl: urlMatch[1], batchId, sessionId, accountId: accountIdMatch[1] };
|
|
19
47
|
}
|
|
20
48
|
/** Throws if the URL doesn't include a sessionId segment (i.e. it's a batch-only URL). */
|
|
21
49
|
export function parseSessionUrl(url) {
|
package/dist/lib/env.js
CHANGED
|
@@ -5,22 +5,20 @@ export const defaultLoadEnvDeps = {
|
|
|
5
5
|
existsSync: fs.existsSync,
|
|
6
6
|
configDotenv: dotenv.config,
|
|
7
7
|
};
|
|
8
|
-
/**
|
|
9
|
-
* Walks up from `startDir` (default: the current working directory) looking for a `.env`
|
|
10
|
-
* file, loading the first one found via `dotenv`. Mirrors the behavior every skill's own
|
|
11
|
-
* `main.ts` used to inline directly.
|
|
12
|
-
*/
|
|
13
8
|
export function loadEnvFromCwd(startDir = process.cwd(), deps = defaultLoadEnvDeps) {
|
|
14
9
|
let dir = startDir;
|
|
15
10
|
while (true) {
|
|
16
11
|
const envPath = path.join(dir, '.env');
|
|
17
12
|
if (deps.existsSync(envPath)) {
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
const preset = new Set(Object.keys(process.env));
|
|
14
|
+
const result = deps.configDotenv({ path: envPath });
|
|
15
|
+
const parsed = result?.parsed ?? {};
|
|
16
|
+
const applied = Object.keys(parsed).filter(name => !preset.has(name));
|
|
17
|
+
return { path: envPath, applied, parsed };
|
|
20
18
|
}
|
|
21
19
|
const parent = path.dirname(dir);
|
|
22
20
|
if (parent === dir)
|
|
23
|
-
return;
|
|
21
|
+
return undefined;
|
|
24
22
|
dir = parent;
|
|
25
23
|
}
|
|
26
24
|
}
|
package/package.json
CHANGED
|
@@ -4,8 +4,16 @@ export interface ParsedApplitoolsUrl {
|
|
|
4
4
|
sessionId: string | null;
|
|
5
5
|
accountId: string;
|
|
6
6
|
}
|
|
7
|
+
export declare const APP_ROUTES: {
|
|
8
|
+
readonly testResults: "test-results";
|
|
9
|
+
readonly batches: "batches";
|
|
10
|
+
readonly baselines: "baselines";
|
|
11
|
+
};
|
|
12
|
+
export declare const BATCH_ROUTE_PREFIX_SOURCE: string;
|
|
7
13
|
/**
|
|
8
14
|
* Parses an Applitools dashboard URL (session or batch) into its components.
|
|
15
|
+
* Accepts any of the `APP_ROUTES` batch-addressing routes; rejections name the
|
|
16
|
+
* accepted forms so the caller can correct the URL without guessing.
|
|
9
17
|
* Does not require a sessionId to be present — use parseSessionUrl/parseBatchUrl
|
|
10
18
|
* for callers that need to enforce one shape or the other.
|
|
11
19
|
*/
|
package/types/lib/env.d.ts
CHANGED
|
@@ -6,9 +6,9 @@ export interface LoadEnvDeps {
|
|
|
6
6
|
}) => unknown;
|
|
7
7
|
}
|
|
8
8
|
export declare const defaultLoadEnvDeps: LoadEnvDeps;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export declare function loadEnvFromCwd(startDir?: string, deps?: LoadEnvDeps):
|
|
9
|
+
export interface LoadedEnv {
|
|
10
|
+
path: string;
|
|
11
|
+
applied: string[];
|
|
12
|
+
parsed: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export declare function loadEnvFromCwd(startDir?: string, deps?: LoadEnvDeps): LoadedEnv | undefined;
|