@sudobility/testomniac_runner 0.0.128 → 0.0.130
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/.env.example +39 -29
- package/.github/workflows/ci-cd.yml +1 -0
- package/bun.lock +4 -4
- package/package.json +3 -3
- package/src/auth/signic-registrar.ts +1 -1
- package/src/browser/chromium.ts +60 -1
- package/src/config/index.test.ts +2 -2
- package/src/config/index.ts +6 -8
package/.env.example
CHANGED
|
@@ -1,67 +1,77 @@
|
|
|
1
1
|
# =============================================================================
|
|
2
|
-
# Testomniac
|
|
2
|
+
# Testomniac Runner Environment Variables
|
|
3
3
|
# =============================================================================
|
|
4
4
|
# Copy this file to .env and fill in your values.
|
|
5
5
|
|
|
6
6
|
# -----------------------------------------------------------------------------
|
|
7
7
|
# Server
|
|
8
8
|
# -----------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
# Port for the health/status HTTP endpoint (Hono server)
|
|
9
11
|
PORT=8030
|
|
10
|
-
|
|
12
|
+
|
|
13
|
+
# Pino log level: trace | debug | info | warn | error | fatal
|
|
11
14
|
LOG_LEVEL=info
|
|
12
15
|
|
|
13
16
|
# -----------------------------------------------------------------------------
|
|
14
|
-
# API
|
|
17
|
+
# API
|
|
15
18
|
# -----------------------------------------------------------------------------
|
|
16
|
-
# URL of the testomniac_api instance
|
|
17
|
-
TESTOMNIAC_API_URL=http://localhost:8027
|
|
18
19
|
|
|
19
|
-
#
|
|
20
|
+
# URL of the testomniac_api instance this runner polls for pending runs
|
|
21
|
+
TESTOMNIAC_API_URL=https://api.testomniac.com
|
|
22
|
+
|
|
23
|
+
# Shared secret for X-Scanner-Key auth. Must match SCANNER_API_KEY in the API's .env.
|
|
20
24
|
# Generate with: openssl rand -hex 32
|
|
21
25
|
SCANNER_API_KEY=
|
|
22
26
|
|
|
23
27
|
# -----------------------------------------------------------------------------
|
|
24
|
-
#
|
|
28
|
+
# Polling & Concurrency
|
|
25
29
|
# -----------------------------------------------------------------------------
|
|
26
|
-
|
|
30
|
+
|
|
31
|
+
# How often (ms) the runner polls the API for pending runs
|
|
27
32
|
SCAN_POLL_INTERVAL_MS=10000
|
|
28
33
|
|
|
34
|
+
# Maximum number of test runs executing in parallel
|
|
35
|
+
MAX_CONCURRENT_RUNNERS=5
|
|
36
|
+
|
|
29
37
|
# -----------------------------------------------------------------------------
|
|
30
|
-
#
|
|
38
|
+
# Browser
|
|
31
39
|
# -----------------------------------------------------------------------------
|
|
32
|
-
|
|
40
|
+
|
|
41
|
+
# Path to Chrome/Chromium binary. Auto-detected from Puppeteer cache if not set.
|
|
42
|
+
# Only set this to override auto-detection (e.g. Docker: /usr/bin/chromium).
|
|
43
|
+
# CHROMIUM_PATH=/usr/bin/chromium
|
|
44
|
+
|
|
45
|
+
# Directory for persistent browser profile (cookies, cache, etc.)
|
|
46
|
+
USER_DATA_DIR=./testomniac-browser-profile
|
|
47
|
+
|
|
48
|
+
# Directory for screenshots and other scan artifacts (JPEG, quality 72)
|
|
49
|
+
ARTIFACT_DIR=./testomniac-artifacts
|
|
33
50
|
|
|
34
51
|
# -----------------------------------------------------------------------------
|
|
35
|
-
# Email Reports (Optional
|
|
52
|
+
# Email Reports (Optional — only needed if sending scan report emails)
|
|
36
53
|
# -----------------------------------------------------------------------------
|
|
37
|
-
|
|
54
|
+
|
|
55
|
+
# Postmark API token for sending emails. Leave empty to disable email reports.
|
|
38
56
|
POSTMARK_SERVER_TOKEN=
|
|
39
57
|
|
|
40
|
-
# Sender email address
|
|
58
|
+
# Sender email address shown on report emails
|
|
41
59
|
POSTMARK_FROM_EMAIL=
|
|
42
60
|
|
|
43
|
-
# Secret
|
|
44
|
-
# If
|
|
61
|
+
# Secret for signing deep-link JWTs embedded in report emails.
|
|
62
|
+
# If empty, deep links use an empty key (insecure — fine for local dev).
|
|
45
63
|
# Generate with: openssl rand -hex 32
|
|
46
64
|
DEEP_LINK_SECRET=
|
|
47
65
|
|
|
48
|
-
# Base URL of the frontend app, used
|
|
66
|
+
# Base URL of the frontend app, used to build deep-link URLs in emails
|
|
49
67
|
APP_BASE_URL=http://localhost:3000
|
|
50
68
|
|
|
51
69
|
# -----------------------------------------------------------------------------
|
|
52
|
-
# Signic Email Verification (Optional
|
|
70
|
+
# Signic Email Verification (Optional — for email-based auth scanning)
|
|
53
71
|
# -----------------------------------------------------------------------------
|
|
54
|
-
SIGNIC_INDEXER_URL=https://api.signic.email/idx
|
|
55
|
-
SIGNIC_WILDDUCK_URL=https://api.signic.email/api
|
|
56
72
|
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
# -----------------------------------------------------------------------------
|
|
60
|
-
# Path to Chromium binary
|
|
61
|
-
CHROMIUM_PATH=/usr/bin/chromium
|
|
62
|
-
|
|
63
|
-
# Directory for browser profile persistence
|
|
64
|
-
USER_DATA_DIR=./browser-profile
|
|
73
|
+
# Signic indexer URL for polling verification/confirmation emails
|
|
74
|
+
SIGNIC_INDEXER_URL=https://api.signic.email/idx
|
|
65
75
|
|
|
66
|
-
#
|
|
67
|
-
|
|
76
|
+
# Signic email API URL for disposable email account management
|
|
77
|
+
SIGNIC_EMAIL_API_URL=https://api.signic.email/api
|
package/bun.lock
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"@noble/curves": "^1.0.0",
|
|
9
9
|
"@noble/hashes": "^1.0.0",
|
|
10
10
|
"@sudobility/signic_sdk": "^0.1.7",
|
|
11
|
-
"@sudobility/testomniac_runner_service": "^0.1.
|
|
12
|
-
"@sudobility/testomniac_types": "^0.0.
|
|
11
|
+
"@sudobility/testomniac_runner_service": "^0.1.128",
|
|
12
|
+
"@sudobility/testomniac_types": "^0.0.67",
|
|
13
13
|
"hono": "^4.7.0",
|
|
14
14
|
"jose": "^6.1.2",
|
|
15
15
|
"openai": "^6.7.0",
|
|
@@ -172,9 +172,9 @@
|
|
|
172
172
|
|
|
173
173
|
"@sudobility/signic_sdk": ["@sudobility/signic_sdk@0.1.7", "", {}, "sha512-5XSgHSVsmyrMQ/ui1nDywwzt9dbRCsaeJ5tX6mKw2ZXbTZ82OsMr+dqDyV9XV3pfy7IHRIZq73af5KBamx72Fw=="],
|
|
174
174
|
|
|
175
|
-
"@sudobility/testomniac_runner_service": ["@sudobility/testomniac_runner_service@0.1.
|
|
175
|
+
"@sudobility/testomniac_runner_service": ["@sudobility/testomniac_runner_service@0.1.128", "", { "peerDependencies": { "@sudobility/testomniac_types": "^0.0.67", "openai": ">=6.0.0", "react": ">=18.0.0" }, "optionalPeers": ["openai", "react"] }, "sha512-YWFjcMmZROSHSNUZLt9czUJeWoYmIj69JCXUP9CD9sw5/UTzFFLwIdkqtAE2lG4RDo/Yb/5heZPmffZ1EfPPqw=="],
|
|
176
176
|
|
|
177
|
-
"@sudobility/testomniac_types": ["@sudobility/testomniac_types@0.0.
|
|
177
|
+
"@sudobility/testomniac_types": ["@sudobility/testomniac_types@0.0.67", "", { "peerDependencies": { "@sudobility/types": "^1.9.62" } }, "sha512-kNBDk7AsFx1rqUWLq4nx0nf1alV9G7U5xbxj8qDvZKa+cCnUSiZFRtDB88ENWQzeIPVJAU8MaVo5yUKVL46eYg=="],
|
|
178
178
|
|
|
179
179
|
"@sudobility/types": ["@sudobility/types@1.9.61", "", {}, "sha512-SODGpstB/iKfK3H/4BvJx/FBcc1h3gutUjGotyxN19VnOfWyzaDoEmW7eyoxOAYhZyXMXagSiii+NIEZvuxKog=="],
|
|
180
180
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sudobility/testomniac_runner",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.130",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"@noble/curves": "^1.0.0",
|
|
25
25
|
"@noble/hashes": "^1.0.0",
|
|
26
26
|
"@sudobility/signic_sdk": "^0.1.7",
|
|
27
|
-
"@sudobility/testomniac_runner_service": "^0.1.
|
|
28
|
-
"@sudobility/testomniac_types": "^0.0.
|
|
27
|
+
"@sudobility/testomniac_runner_service": "^0.1.128",
|
|
28
|
+
"@sudobility/testomniac_types": "^0.0.67",
|
|
29
29
|
"hono": "^4.7.0",
|
|
30
30
|
"jose": "^6.1.2",
|
|
31
31
|
"openai": "^6.7.0",
|
|
@@ -51,7 +51,7 @@ export async function autoRegister(
|
|
|
51
51
|
|
|
52
52
|
const client = new SignicClient({
|
|
53
53
|
indexerUrl: config.signicIndexerUrl,
|
|
54
|
-
wildduckUrl: config.
|
|
54
|
+
wildduckUrl: config.signicEmailApiUrl,
|
|
55
55
|
signMessage: async (message: string) => signEthMessage(message, privateKey),
|
|
56
56
|
});
|
|
57
57
|
|
package/src/browser/chromium.ts
CHANGED
|
@@ -1,7 +1,66 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
1
4
|
import puppeteer, { type Browser, type Page } from "puppeteer-core";
|
|
2
5
|
import type { Config } from "../config/index";
|
|
3
6
|
import type { Screen } from "@sudobility/testomniac_types";
|
|
4
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Finds the latest Chrome for Testing binary in the Puppeteer cache.
|
|
10
|
+
* Falls back to common system paths, then to the config value.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveChromiumPath(configPath: string): string {
|
|
13
|
+
// Explicit env var / config always wins
|
|
14
|
+
if (process.env.CHROMIUM_PATH) return process.env.CHROMIUM_PATH;
|
|
15
|
+
|
|
16
|
+
// Scan Puppeteer cache for the latest Chrome for Testing
|
|
17
|
+
const cacheDir = join(homedir(), ".cache", "puppeteer", "chrome");
|
|
18
|
+
if (existsSync(cacheDir)) {
|
|
19
|
+
const platform =
|
|
20
|
+
process.platform === "darwin"
|
|
21
|
+
? process.arch === "arm64"
|
|
22
|
+
? "mac_arm"
|
|
23
|
+
: "mac-x64"
|
|
24
|
+
: "linux";
|
|
25
|
+
const versions = readdirSync(cacheDir)
|
|
26
|
+
.filter(d => d.startsWith(platform + "-"))
|
|
27
|
+
.sort()
|
|
28
|
+
.reverse();
|
|
29
|
+
|
|
30
|
+
for (const version of versions) {
|
|
31
|
+
const candidates =
|
|
32
|
+
process.platform === "darwin"
|
|
33
|
+
? [
|
|
34
|
+
join(
|
|
35
|
+
cacheDir,
|
|
36
|
+
version,
|
|
37
|
+
"chrome-mac-arm64",
|
|
38
|
+
"Google Chrome for Testing.app",
|
|
39
|
+
"Contents",
|
|
40
|
+
"MacOS",
|
|
41
|
+
"Google Chrome for Testing"
|
|
42
|
+
),
|
|
43
|
+
join(
|
|
44
|
+
cacheDir,
|
|
45
|
+
version,
|
|
46
|
+
"chrome-mac-x64",
|
|
47
|
+
"Google Chrome for Testing.app",
|
|
48
|
+
"Contents",
|
|
49
|
+
"MacOS",
|
|
50
|
+
"Google Chrome for Testing"
|
|
51
|
+
),
|
|
52
|
+
]
|
|
53
|
+
: [join(cacheDir, version, "chrome-linux64", "chrome")];
|
|
54
|
+
|
|
55
|
+
for (const candidate of candidates) {
|
|
56
|
+
if (existsSync(candidate)) return candidate;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return configPath;
|
|
62
|
+
}
|
|
63
|
+
|
|
5
64
|
export class ChromiumManager {
|
|
6
65
|
private browser: Browser | null = null;
|
|
7
66
|
|
|
@@ -9,7 +68,7 @@ export class ChromiumManager {
|
|
|
9
68
|
|
|
10
69
|
async launch(): Promise<Browser> {
|
|
11
70
|
this.browser = await puppeteer.launch({
|
|
12
|
-
executablePath: this.config.chromiumPath,
|
|
71
|
+
executablePath: resolveChromiumPath(this.config.chromiumPath),
|
|
13
72
|
userDataDir: this.config.userDataDir,
|
|
14
73
|
headless: true,
|
|
15
74
|
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
package/src/config/index.test.ts
CHANGED
|
@@ -17,7 +17,7 @@ describe("config", () => {
|
|
|
17
17
|
|
|
18
18
|
it("uses defaults for optional values", () => {
|
|
19
19
|
const config = loadConfig();
|
|
20
|
-
expect(config.artifactDir).toBe("./artifacts");
|
|
21
|
-
expect(config.userDataDir).toBe("./browser-profile");
|
|
20
|
+
expect(config.artifactDir).toBe("./testomniac-artifacts");
|
|
21
|
+
expect(config.userDataDir).toBe("./testomniac-browser-profile");
|
|
22
22
|
});
|
|
23
23
|
});
|
package/src/config/index.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
export interface Config {
|
|
2
2
|
apiUrl: string;
|
|
3
3
|
scannerApiKey: string;
|
|
4
|
-
openaiApiKey: string;
|
|
5
4
|
postmarkServerToken: string;
|
|
6
5
|
postmarkFromEmail: string;
|
|
7
6
|
deepLinkSecret: string;
|
|
8
7
|
appBaseUrl: string;
|
|
9
8
|
signicIndexerUrl: string;
|
|
10
|
-
|
|
9
|
+
signicEmailApiUrl: string;
|
|
11
10
|
chromiumPath: string;
|
|
12
11
|
userDataDir: string;
|
|
13
12
|
artifactDir: string;
|
|
@@ -16,20 +15,19 @@ export interface Config {
|
|
|
16
15
|
|
|
17
16
|
export function loadConfig(): Config {
|
|
18
17
|
return {
|
|
19
|
-
apiUrl: process.env.TESTOMNIAC_API_URL || "
|
|
18
|
+
apiUrl: process.env.TESTOMNIAC_API_URL || "https://api.testomniac.com",
|
|
20
19
|
scannerApiKey: process.env.SCANNER_API_KEY || "",
|
|
21
|
-
openaiApiKey: process.env.OPENAI_API_KEY || "",
|
|
22
20
|
postmarkServerToken: process.env.POSTMARK_SERVER_TOKEN || "",
|
|
23
21
|
postmarkFromEmail: process.env.POSTMARK_FROM_EMAIL || "",
|
|
24
22
|
deepLinkSecret: process.env.DEEP_LINK_SECRET || "",
|
|
25
23
|
appBaseUrl: process.env.APP_BASE_URL || "http://localhost:3000",
|
|
26
24
|
signicIndexerUrl:
|
|
27
25
|
process.env.SIGNIC_INDEXER_URL || "https://api.signic.email/idx",
|
|
28
|
-
|
|
29
|
-
process.env.
|
|
26
|
+
signicEmailApiUrl:
|
|
27
|
+
process.env.SIGNIC_EMAIL_API_URL || "https://api.signic.email/api",
|
|
30
28
|
chromiumPath: process.env.CHROMIUM_PATH || "/usr/bin/chromium",
|
|
31
|
-
userDataDir: process.env.USER_DATA_DIR || "./browser-profile",
|
|
32
|
-
artifactDir: process.env.ARTIFACT_DIR || "./artifacts",
|
|
29
|
+
userDataDir: process.env.USER_DATA_DIR || "./testomniac-browser-profile",
|
|
30
|
+
artifactDir: process.env.ARTIFACT_DIR || "./testomniac-artifacts",
|
|
33
31
|
maxConcurrentRunners: Number(process.env.MAX_CONCURRENT_RUNNERS ?? 5),
|
|
34
32
|
};
|
|
35
33
|
}
|