@flowspec-qa/runner-core 0.1.0

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,50 @@
1
+ import { AxiosRequestConfig } from 'axios';
2
+ export declare const apiClient: {
3
+ get<T>(url: string, cfg?: AxiosRequestConfig): Promise<T>;
4
+ post<T>(url: string, data?: unknown, cfg?: AxiosRequestConfig): Promise<T>;
5
+ getStream(url: string): Promise<NodeJS.ReadableStream>;
6
+ getArrayBuffer(url: string): Promise<Buffer>;
7
+ /** Update base URL at runtime (e.g. pointing to a self-hosted instance) */
8
+ setBaseUrl(url: string): void;
9
+ };
10
+ export interface Project {
11
+ id: string;
12
+ name: string;
13
+ entity_count?: number;
14
+ }
15
+ export interface Suite {
16
+ entity_id: string;
17
+ entity_title: string;
18
+ suite_id: string;
19
+ framework: 'playwright' | 'cypress' | 'maestro';
20
+ mode?: string;
21
+ status?: string;
22
+ created_at?: string;
23
+ }
24
+ export declare const FlowSpecQAApi: {
25
+ listProjects(): Promise<Project[]>;
26
+ listSuites(entityId?: string): Promise<Suite[]>;
27
+ /** Download the ZIP of generated test files for a suite (suite_id = TestSuite.id) */
28
+ downloadSuiteZip(suiteId: string): Promise<Buffer>;
29
+ submitRunResult(payload: RunResultPayload): Promise<{
30
+ ok: boolean;
31
+ exec_id: string;
32
+ }>;
33
+ };
34
+ export interface RunResultPayload {
35
+ run_id: string;
36
+ entity_id: string;
37
+ framework: string;
38
+ env_label: string;
39
+ results: TestResultItem[];
40
+ started_at: string;
41
+ finished_at: string;
42
+ runner_version: string;
43
+ }
44
+ export interface TestResultItem {
45
+ name: string;
46
+ status: 'pass' | 'fail' | 'skip';
47
+ duration_ms: number;
48
+ error?: string | null;
49
+ }
50
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA,OAAc,EAAiB,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAmCjE,eAAO,MAAM,SAAS;QACV,CAAC,OAAO,MAAM,QAAQ,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;SAIpD,CAAC,OAAO,MAAM,SAAS,OAAO,QAAQ,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;mBAI3D,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC;wBAIlC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlD,2EAA2E;oBAC3D,MAAM,GAAG,IAAI;CAI9B,CAAC;AAIF,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,KAAK;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,aAAa;oBACF,OAAO,CAAC,OAAO,EAAE,CAAC;0BAIZ,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAQrD,qFAAqF;8BACrD,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;6BAIzB,gBAAgB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAG5F,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB"}
@@ -0,0 +1,70 @@
1
+ import axios from 'axios';
2
+ import { config } from './config.js';
3
+ // Lazy singleton — created on first use so baseUrl is read after config is loaded
4
+ let _client = null;
5
+ function getClient() {
6
+ if (!_client) {
7
+ _client = axios.create({
8
+ baseURL: config.get('baseUrl'),
9
+ timeout: 30_000,
10
+ headers: { 'Content-Type': 'application/json' },
11
+ });
12
+ // Attach Bearer token (API Key) to every request
13
+ _client.interceptors.request.use((req) => {
14
+ const apiKey = config.get('apiKey');
15
+ if (apiKey) {
16
+ req.headers['Authorization'] = `Bearer ${apiKey}`;
17
+ }
18
+ return req;
19
+ });
20
+ // Normalize error messages
21
+ _client.interceptors.response.use((res) => res, (err) => {
22
+ const msg = err.response?.data?.detail || err.response?.data?.message || err.message;
23
+ return Promise.reject(new Error(msg));
24
+ });
25
+ }
26
+ return _client;
27
+ }
28
+ export const apiClient = {
29
+ async get(url, cfg) {
30
+ const res = await getClient().get(url, cfg);
31
+ return res.data;
32
+ },
33
+ async post(url, data, cfg) {
34
+ const res = await getClient().post(url, data, cfg);
35
+ return res.data;
36
+ },
37
+ async getStream(url) {
38
+ const res = await getClient().get(url, { responseType: 'stream' });
39
+ return res.data;
40
+ },
41
+ async getArrayBuffer(url) {
42
+ const res = await getClient().get(url, { responseType: 'arraybuffer' });
43
+ return Buffer.from(res.data);
44
+ },
45
+ /** Update base URL at runtime (e.g. pointing to a self-hosted instance) */
46
+ setBaseUrl(url) {
47
+ config.set('baseUrl', url);
48
+ _client = null; // force recreation
49
+ },
50
+ };
51
+ export const FlowSpecQAApi = {
52
+ async listProjects() {
53
+ return apiClient.get('/api/config/projects');
54
+ },
55
+ async listSuites(entityId) {
56
+ const url = entityId
57
+ ? `/api/automation/suites?entity_id=${entityId}`
58
+ : '/api/automation/suites';
59
+ const resp = await apiClient.get(url);
60
+ return resp.suites ?? [];
61
+ },
62
+ /** Download the ZIP of generated test files for a suite (suite_id = TestSuite.id) */
63
+ async downloadSuiteZip(suiteId) {
64
+ return apiClient.getArrayBuffer(`/api/automation/download-suite/${suiteId}`);
65
+ },
66
+ async submitRunResult(payload) {
67
+ return apiClient.post('/api/automation/submit-run-result', payload);
68
+ },
69
+ };
70
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
package/dist/auth.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ export interface AuthStatus {
2
+ authenticated: boolean;
3
+ maskedKey?: string;
4
+ }
5
+ /**
6
+ * Store the API Key locally and test it against the SaaS.
7
+ * Keys use the format: sk-smrt-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
8
+ */
9
+ export declare function login(apiKey: string): Promise<void>;
10
+ export declare function logout(): void;
11
+ export declare function getAuthStatus(): AuthStatus;
12
+ /**
13
+ * Return the API Key to use as Bearer token.
14
+ * The SaaS get_api_key_context dependency accepts: Authorization: Bearer sk-smrt-xxx
15
+ */
16
+ export declare function getBearerToken(): string;
17
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQzD;AAED,wBAAgB,MAAM,IAAI,IAAI,CAE7B;AAED,wBAAgB,aAAa,IAAI,UAAU,CAO1C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAMvC"}
package/dist/auth.js ADDED
@@ -0,0 +1,38 @@
1
+ import { config } from './config.js';
2
+ /**
3
+ * Store the API Key locally and test it against the SaaS.
4
+ * Keys use the format: sk-smrt-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5
+ */
6
+ export async function login(apiKey) {
7
+ if (!apiKey.startsWith('sk-smrt-')) {
8
+ throw new Error('Invalid API Key format. Keys should start with "sk-smrt-"');
9
+ }
10
+ config.set('apiKey', apiKey);
11
+ // Validate immediately by making a test call
12
+ const { apiClient } = await import('./api-client.js');
13
+ await apiClient.get('/api/config/projects'); // throws if key is invalid
14
+ }
15
+ export function logout() {
16
+ config.set('apiKey', undefined);
17
+ }
18
+ export function getAuthStatus() {
19
+ const apiKey = config.get('apiKey');
20
+ if (!apiKey)
21
+ return { authenticated: false };
22
+ return {
23
+ authenticated: true,
24
+ maskedKey: apiKey.slice(0, 12) + '*'.repeat(Math.max(0, apiKey.length - 12)),
25
+ };
26
+ }
27
+ /**
28
+ * Return the API Key to use as Bearer token.
29
+ * The SaaS get_api_key_context dependency accepts: Authorization: Bearer sk-smrt-xxx
30
+ */
31
+ export function getBearerToken() {
32
+ const apiKey = config.get('apiKey');
33
+ if (!apiKey) {
34
+ throw new Error('Not authenticated. Run: flowspecqa auth login');
35
+ }
36
+ return apiKey;
37
+ }
38
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAOrC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,MAAc;IACxC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC7B,6CAA6C;IAC7C,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACtD,MAAM,SAAS,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC,2BAA2B;AAC1E,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;IAC7C,OAAO;QACL,aAAa,EAAE,IAAI;QACnB,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;KAC7E,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface RunnerConfig {
2
+ apiKey?: string;
3
+ runnerToken?: string;
4
+ runnerTokenExpiry?: string;
5
+ baseUrl: string;
6
+ suiteCacheDir: string;
7
+ sanitizerPatterns: string[];
8
+ }
9
+ export declare const config: {
10
+ get<K extends keyof RunnerConfig>(key: K): RunnerConfig[K];
11
+ set<K extends keyof RunnerConfig>(key: K, value: RunnerConfig[K]): void;
12
+ getAll(): RunnerConfig;
13
+ clear(): void;
14
+ readonly configPath: string;
15
+ };
16
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAoBD,eAAO,MAAM,MAAM;QACb,CAAC,SAAS,MAAM,YAAY,OAAO,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;QAItD,CAAC,SAAS,MAAM,YAAY,OAAO,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI;cAI7D,YAAY;aAIb,IAAI;yBAIK,MAAM;CAGzB,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,37 @@
1
+ import Conf from 'conf';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ const DEFAULT_CONFIG = {
5
+ baseUrl: 'https://www.flowspecqa.com',
6
+ suiteCacheDir: path.join(os.homedir(), '.flowspecqa', 'suites'),
7
+ sanitizerPatterns: [
8
+ // Strip URLs, IPs, auth tokens from error messages before upload
9
+ 'https?://[^\\s]+',
10
+ '\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b',
11
+ 'Bearer\\s+[A-Za-z0-9.\\-_]+',
12
+ 'Authorization:\\s*[^\\r\\n]+',
13
+ 'password["\']?\\s*[:=]\\s*["\']?[^"\'\\s&]+',
14
+ ],
15
+ };
16
+ const conf = new Conf({
17
+ projectName: 'flowspecqa-runner',
18
+ defaults: DEFAULT_CONFIG,
19
+ });
20
+ export const config = {
21
+ get(key) {
22
+ return conf.get(key);
23
+ },
24
+ set(key, value) {
25
+ conf.set(key, value);
26
+ },
27
+ getAll() {
28
+ return conf.store;
29
+ },
30
+ clear() {
31
+ conf.clear();
32
+ },
33
+ get configPath() {
34
+ return conf.path;
35
+ },
36
+ };
37
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AAWpB,MAAM,cAAc,GAAiB;IACnC,OAAO,EAAE,4BAA4B;IACrC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,QAAQ,CAAC;IAC/D,iBAAiB,EAAE;QACjB,iEAAiE;QACjE,kBAAkB;QAClB,iDAAiD;QACjD,6BAA6B;QAC7B,8BAA8B;QAC9B,6CAA6C;KAC9C;CACF,CAAC;AAEF,MAAM,IAAI,GAAG,IAAI,IAAI,CAAe;IAClC,WAAW,EAAE,mBAAmB;IAChC,QAAQ,EAAE,cAAc;CACzB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,GAAG,CAA+B,GAAM;QACtC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAoB,CAAC;IAC1C,CAAC;IAED,GAAG,CAA+B,GAAM,EAAE,KAAsB;QAC9D,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACvB,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,KAAqB,CAAC;IACpC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;CACF,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { TestResultItem } from './api-client.js';
2
+ export interface ExecutionOptions {
3
+ env?: Record<string, string>;
4
+ timeout?: number;
5
+ workers?: number;
6
+ headed?: boolean;
7
+ reporter?: string;
8
+ grepPattern?: string;
9
+ }
10
+ export interface ExecutionResult {
11
+ exitCode: number;
12
+ results: TestResultItem[];
13
+ startedAt: Date;
14
+ finishedAt: Date;
15
+ stdout: string;
16
+ stderr: string;
17
+ }
18
+ /**
19
+ * Execute a Playwright test suite and parse the results.
20
+ */
21
+ export declare function executePlaywright(suiteDir: string, options?: ExecutionOptions): Promise<ExecutionResult>;
22
+ /**
23
+ * Execute a Cypress test suite.
24
+ */
25
+ export declare function executeCypress(suiteDir: string, options?: ExecutionOptions): Promise<ExecutionResult>;
26
+ /**
27
+ * Execute a Maestro test suite (iOS/Android YAML flows) and parse JUnit XML results.
28
+ * Requires: Maestro CLI installed globally (`curl -Ls 'https://get.maestro.mobile.dev' | bash`)
29
+ * No npm install step — Maestro uses its own runtime.
30
+ */
31
+ export declare function executeMaestro(suiteDir: string, options?: ExecutionOptions): Promise<ExecutionResult>;
32
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CA2C1B;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAkC1B;AA8DD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAqC1B"}
@@ -0,0 +1,221 @@
1
+ import { execa } from 'execa';
2
+ import path from 'path';
3
+ import { existsSync } from 'fs';
4
+ /**
5
+ * Execute a Playwright test suite and parse the results.
6
+ */
7
+ export async function executePlaywright(suiteDir, options = {}) {
8
+ const startedAt = new Date();
9
+ // Install deps if node_modules missing
10
+ if (!isNodeModulesPresent(suiteDir)) {
11
+ await execa('npm', ['install', '--prefer-offline'], {
12
+ cwd: suiteDir,
13
+ env: { ...process.env, ...(options.env ?? {}) },
14
+ });
15
+ }
16
+ const args = ['playwright', 'test',
17
+ '--reporter=json',
18
+ ...(options.workers ? [`--workers=${options.workers}`] : []),
19
+ ...(options.headed ? ['--headed'] : []),
20
+ ...(options.grepPattern ? [`--grep`, options.grepPattern] : []),
21
+ ];
22
+ let stdout = '';
23
+ let stderr = '';
24
+ let exitCode = 0;
25
+ try {
26
+ const result = await execa('npx', args, {
27
+ cwd: suiteDir,
28
+ env: { ...process.env, ...(options.env ?? {}) },
29
+ timeout: options.timeout ?? 600_000,
30
+ reject: false, // don't throw on test failures
31
+ });
32
+ stdout = result.stdout;
33
+ stderr = result.stderr;
34
+ exitCode = result.exitCode ?? 1;
35
+ }
36
+ catch (err) {
37
+ const e = err;
38
+ stdout = e.stdout ?? '';
39
+ stderr = e.stderr ?? e.message;
40
+ exitCode = e.exitCode ?? 1;
41
+ }
42
+ const finishedAt = new Date();
43
+ const results = parsePlaywrightJson(stdout);
44
+ return { exitCode, results, startedAt, finishedAt, stdout, stderr };
45
+ }
46
+ /**
47
+ * Execute a Cypress test suite.
48
+ */
49
+ export async function executeCypress(suiteDir, options = {}) {
50
+ const startedAt = new Date();
51
+ if (!isNodeModulesPresent(suiteDir)) {
52
+ await execa('npm', ['install', '--prefer-offline'], { cwd: suiteDir });
53
+ }
54
+ const args = ['cypress', 'run', '--reporter=json',
55
+ ...(options.headed ? ['--headed'] : ['--headless']),
56
+ ];
57
+ let stdout = '';
58
+ let stderr = '';
59
+ let exitCode = 0;
60
+ try {
61
+ const result = await execa('npx', args, {
62
+ cwd: suiteDir,
63
+ env: { ...process.env, ...(options.env ?? {}) },
64
+ timeout: options.timeout ?? 600_000,
65
+ reject: false,
66
+ });
67
+ stdout = result.stdout;
68
+ stderr = result.stderr;
69
+ exitCode = result.exitCode ?? 1;
70
+ }
71
+ catch (err) {
72
+ const e = err;
73
+ stdout = e.stdout ?? '';
74
+ stderr = e.stderr ?? e.message;
75
+ exitCode = e.exitCode ?? 1;
76
+ }
77
+ const finishedAt = new Date();
78
+ return { exitCode, results: parseCypressJson(stdout), startedAt, finishedAt, stdout, stderr };
79
+ }
80
+ // ─── Parsers ─────────────────────────────────────────────────────────────────
81
+ function parsePlaywrightJson(stdout) {
82
+ try {
83
+ // Playwright --reporter=json outputs JSON on stdout
84
+ const json = extractJson(stdout);
85
+ if (!json)
86
+ return [];
87
+ const suites = json.suites ?? [];
88
+ const items = [];
89
+ collectPlaywrightSpecs(suites, items);
90
+ return items;
91
+ }
92
+ catch {
93
+ return [];
94
+ }
95
+ }
96
+ function collectPlaywrightSpecs(suites, items) {
97
+ for (const suite of suites) {
98
+ for (const spec of suite.specs ?? []) {
99
+ for (const test of spec.tests ?? []) {
100
+ const result = test.results?.[0];
101
+ items.push({
102
+ name: spec.title,
103
+ status: result?.status === 'passed' ? 'pass' : result?.status === 'skipped' ? 'skip' : 'fail',
104
+ duration_ms: result?.duration ?? 0,
105
+ error: result?.error?.message ?? null,
106
+ });
107
+ }
108
+ }
109
+ collectPlaywrightSpecs(suite.suites ?? [], items);
110
+ }
111
+ }
112
+ function parseCypressJson(stdout) {
113
+ try {
114
+ const json = extractJson(stdout);
115
+ if (!json)
116
+ return [];
117
+ return (json.results ?? []).flatMap((r) => (r.tests ?? []).map((t) => ({
118
+ name: t.fullTitle ?? t.title,
119
+ status: t.state === 'passed' ? 'pass' : t.state === 'pending' ? 'skip' : 'fail',
120
+ duration_ms: t.duration ?? 0,
121
+ error: t.err?.message ?? null,
122
+ })));
123
+ }
124
+ catch {
125
+ return [];
126
+ }
127
+ }
128
+ function extractJson(text) {
129
+ const match = text.match(/\{[\s\S]*\}/);
130
+ if (!match)
131
+ return null;
132
+ return JSON.parse(match[0]);
133
+ }
134
+ function isNodeModulesPresent(dir) {
135
+ return existsSync(path.join(dir, 'node_modules'));
136
+ }
137
+ /**
138
+ * Execute a Maestro test suite (iOS/Android YAML flows) and parse JUnit XML results.
139
+ * Requires: Maestro CLI installed globally (`curl -Ls 'https://get.maestro.mobile.dev' | bash`)
140
+ * No npm install step — Maestro uses its own runtime.
141
+ */
142
+ export async function executeMaestro(suiteDir, options = {}) {
143
+ const startedAt = new Date();
144
+ const reportPath = path.join(suiteDir, 'report.xml');
145
+ const flowsDir = path.join(suiteDir, 'flows');
146
+ const args = [
147
+ 'test',
148
+ '--format', 'junit',
149
+ '--output', reportPath,
150
+ existsSync(flowsDir) ? flowsDir : suiteDir,
151
+ ];
152
+ let stdout = '';
153
+ let stderr = '';
154
+ let exitCode = 0;
155
+ try {
156
+ const proc = await execa('maestro', args, {
157
+ cwd: suiteDir,
158
+ env: { ...process.env, ...(options.env ?? {}) },
159
+ reject: false,
160
+ timeout: options.timeout ?? 10 * 60 * 1000,
161
+ });
162
+ stdout = proc.stdout ?? '';
163
+ stderr = proc.stderr ?? '';
164
+ exitCode = proc.exitCode ?? 0;
165
+ }
166
+ catch (err) {
167
+ const execaErr = err;
168
+ stdout = execaErr.stdout ?? '';
169
+ stderr = execaErr.stderr ?? '';
170
+ exitCode = execaErr.exitCode ?? 1;
171
+ }
172
+ const finishedAt = new Date();
173
+ const results = parseMaestroXml(reportPath);
174
+ return { exitCode, results, startedAt, finishedAt, stdout, stderr };
175
+ }
176
+ /**
177
+ * Parse Maestro JUnit XML report into TestResultItem[].
178
+ * Format: <testsuite><testcase name="..." time="2.3"><failure message="..."/></testcase></testsuite>
179
+ */
180
+ function parseMaestroXml(reportPath) {
181
+ if (!existsSync(reportPath))
182
+ return [];
183
+ try {
184
+ const { XMLParser } = require('fast-xml-parser');
185
+ const { readFileSync } = require('fs');
186
+ const xml = readFileSync(reportPath, 'utf-8');
187
+ const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' });
188
+ const doc = parser.parse(xml);
189
+ // Support both <testsuites> (wrapper) and bare <testsuite>
190
+ const suites = doc?.testsuites?.testsuite ?? doc?.testsuite;
191
+ const suiteArray = Array.isArray(suites) ? suites : suites ? [suites] : [];
192
+ const items = [];
193
+ for (const suite of suiteArray) {
194
+ const testcases = suite?.testcase;
195
+ const cases = Array.isArray(testcases) ? testcases : testcases ? [testcases] : [];
196
+ for (const tc of cases) {
197
+ const name = tc['@_name'] ?? tc['@_classname'] ?? 'unknown';
198
+ const timeStr = tc['@_time'] ?? '0';
199
+ const duration_ms = Math.round(parseFloat(timeStr) * 1000);
200
+ const hasFailure = tc.failure !== undefined;
201
+ const hasSkipped = tc.skipped !== undefined;
202
+ const failureMsg = typeof tc.failure === 'object'
203
+ ? tc.failure['@_message'] ?? null
204
+ : typeof tc.failure === 'string'
205
+ ? tc.failure
206
+ : null;
207
+ items.push({
208
+ name,
209
+ status: hasFailure ? 'fail' : hasSkipped ? 'skip' : 'pass',
210
+ duration_ms,
211
+ error: hasFailure ? failureMsg : null,
212
+ });
213
+ }
214
+ }
215
+ return items;
216
+ }
217
+ catch {
218
+ return [];
219
+ }
220
+ }
221
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { config, type RunnerConfig } from './config.js';
2
+ export { login, logout, getAuthStatus, getBearerToken, type AuthStatus } from './auth.js';
3
+ export { apiClient, FlowSpecQAApi, type Project, type Suite, type RunResultPayload, type TestResultItem } from './api-client.js';
4
+ export { downloadSuite, listCachedSuites, evictSuite, type LocalSuite } from './suite-manager.js';
5
+ export { executePlaywright, executeCypress, executeMaestro, type ExecutionOptions, type ExecutionResult } from './executor.js';
6
+ export { sanitizeResults, assertNoSecrets } from './result-sanitizer.js';
7
+ export { uploadResults, type UploadOptions } from './uploader.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjI,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAC/H,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ // @flowspec-qa/runner-core — public API
2
+ export { config } from './config.js';
3
+ export { login, logout, getAuthStatus, getBearerToken } from './auth.js';
4
+ export { apiClient, FlowSpecQAApi } from './api-client.js';
5
+ export { downloadSuite, listCachedSuites, evictSuite } from './suite-manager.js';
6
+ export { executePlaywright, executeCypress, executeMaestro } from './executor.js';
7
+ export { sanitizeResults, assertNoSecrets } from './result-sanitizer.js';
8
+ export { uploadResults } from './uploader.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,OAAO,EAAE,MAAM,EAAqB,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,EAAmB,MAAM,WAAW,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,aAAa,EAAwE,MAAM,iBAAiB,CAAC;AACjI,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAmB,MAAM,oBAAoB,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAA+C,MAAM,eAAe,CAAC;AAC/H,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAsB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { TestResultItem } from './api-client.js';
2
+ /**
3
+ * Sanitize test results before uploading to FlowSpecQA SaaS.
4
+ *
5
+ * Strips:
6
+ * - HTTP/HTTPS URLs (base URLs of the client's environments)
7
+ * - IP addresses
8
+ * - Authorization / Bearer tokens
9
+ * - Password patterns (key=value)
10
+ * - Anything matching custom patterns in config
11
+ *
12
+ * The test name, status, and duration are NEVER modified.
13
+ * Only `error` messages are sanitized.
14
+ */
15
+ export declare function sanitizeResults(results: TestResultItem[]): TestResultItem[];
16
+ /**
17
+ * Validate that no obvious secrets leaked through (belt-and-suspenders check).
18
+ * Throws if any result still contains a pattern that looks like a secret.
19
+ */
20
+ export declare function assertNoSecrets(results: TestResultItem[]): void;
21
+ //# sourceMappingURL=result-sanitizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-sanitizer.d.ts","sourceRoot":"","sources":["../src/result-sanitizer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAO3E;AAcD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,IAAI,CAkB/D"}
@@ -0,0 +1,54 @@
1
+ import { config } from './config.js';
2
+ /**
3
+ * Sanitize test results before uploading to FlowSpecQA SaaS.
4
+ *
5
+ * Strips:
6
+ * - HTTP/HTTPS URLs (base URLs of the client's environments)
7
+ * - IP addresses
8
+ * - Authorization / Bearer tokens
9
+ * - Password patterns (key=value)
10
+ * - Anything matching custom patterns in config
11
+ *
12
+ * The test name, status, and duration are NEVER modified.
13
+ * Only `error` messages are sanitized.
14
+ */
15
+ export function sanitizeResults(results) {
16
+ const patterns = config.get('sanitizerPatterns').map((p) => new RegExp(p, 'gi'));
17
+ return results.map((r) => ({
18
+ ...r,
19
+ error: r.error ? sanitizeString(r.error, patterns) : null,
20
+ }));
21
+ }
22
+ function sanitizeString(text, patterns) {
23
+ let sanitized = text;
24
+ for (const pattern of patterns) {
25
+ sanitized = sanitized.replace(pattern, '[REDACTED]');
26
+ }
27
+ // Additional pass: truncate very long error messages to 1500 chars
28
+ if (sanitized.length > 1500) {
29
+ sanitized = sanitized.slice(0, 1500) + '... [truncated]';
30
+ }
31
+ return sanitized;
32
+ }
33
+ /**
34
+ * Validate that no obvious secrets leaked through (belt-and-suspenders check).
35
+ * Throws if any result still contains a pattern that looks like a secret.
36
+ */
37
+ export function assertNoSecrets(results) {
38
+ const dangerPatterns = [
39
+ /https?:\/\/[a-z0-9.-]+\.(internal|local|corp|dev|staging|test)[/:]?/i,
40
+ /Bearer\s+[A-Za-z0-9.\-_]{20,}/,
41
+ /\bpassword\b/i,
42
+ ];
43
+ for (const r of results) {
44
+ if (!r.error)
45
+ continue;
46
+ for (const p of dangerPatterns) {
47
+ if (p.test(r.error)) {
48
+ throw new Error(`Security check failed: error message for test "${r.name}" may contain sensitive data. ` +
49
+ `Run with --skip-security-check to bypass (not recommended).`);
50
+ }
51
+ }
52
+ }
53
+ }
54
+ //# sourceMappingURL=result-sanitizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-sanitizer.js","sourceRoot":"","sources":["../src/result-sanitizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,OAAyB;IACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAEjF,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzB,GAAG,CAAC;QACJ,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI;KAC1D,CAAC,CAAC,CAAC;AACN,CAAC;AAED,SAAS,cAAc,CAAC,IAAY,EAAE,QAAkB;IACtD,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACvD,CAAC;IACD,mEAAmE;IACnE,IAAI,SAAS,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;QAC5B,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,iBAAiB,CAAC;IAC3D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAyB;IACvD,MAAM,cAAc,GAAG;QACrB,sEAAsE;QACtE,+BAA+B;QAC/B,eAAe;KAChB,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,CAAC,KAAK;YAAE,SAAS;QACvB,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;YAC/B,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CACb,kDAAkD,CAAC,CAAC,IAAI,gCAAgC;oBACxF,6DAA6D,CAC9D,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}