import Credentials from '../models/credentials'; import TestingBotError from '../models/testingbot_error'; import Upload from '../upload'; import Spinner from '../ui/spinner'; import type { JsonOutput, JsonRunResult, RunOutcome } from '../utils/json_output'; /** * Common interface for run information shared by all providers */ export interface BaseRunInfo { id: number; status: 'WAITING' | 'READY' | 'DONE' | 'FAILED'; capabilities: { deviceName: string; platformName: string; version?: string; }; success: number; report?: string; } /** * Result of a provider's run(). `outcome` tells the CLI whether a `success` * of false means the tests failed (exit 2) or the command itself failed * (exit 1); `success` alone cannot distinguish the two. */ export interface ProviderResult { success: boolean; outcome: RunOutcome; runs: TRun[]; /** Message of the error that ended the command, when outcome is `error`. */ error?: string; } /** The subset of run info needed to render a run as JSON. */ export interface JsonRunSource { id: number; status: string; capabilities: { deviceName: string; platformName: string; version?: string; }; success: number | boolean; report?: string; } /** * Common interface for provider options */ export interface BaseProviderOptions { quiet?: boolean; reportOutputDir?: string; dryRun?: boolean; tunnel?: boolean; tunnelIdentifier?: string; } /** * Abstract base class for test providers (Espresso, XCUITest, Maestro) * Contains common functionality shared across all providers. */ export default abstract class BaseProvider { protected readonly MIN_POLL_INTERVAL_MS: number; protected readonly MAX_POLL_INTERVAL_MS: number; protected readonly POLL_BACKOFF_MULTIPLIER: number; protected readonly MAX_POLL_DURATION_MS: number; /** * Whether a run/flow `success` field reported by the API indicates success. * The API is inconsistent about the representation: some endpoints return a * boolean (`true`/`false`) and others a number (`1`/`0`). A strict * `success === 1` check silently treats a passing run reported as `true` as a * failure, which is why a test that passes on TestingBot was shown as failed * by the CLI. Accept both representations. */ protected isRunSuccessful(success: number | boolean | undefined): boolean; /** * Returns the next polling interval based on whether the status payload * changed since the last poll. Resets to the minimum on change; otherwise * multiplies the current interval by the backoff factor up to the maximum. */ protected computeNextPollInterval(currentIntervalMs: number, changed: boolean): number; /** Identifier used in the `provider` field of --json output. */ protected abstract readonly jsonProvider: JsonOutput['provider']; /** * Dashboard URL for the current app, or for one of its runs. Providers that * expose a members page override this; the default omits the field. */ protected dashboardUrl(runId?: number): string | undefined; /** * Renders one run as it appears in --json output. Subclasses extend the * base shape (e.g. Maestro adds per-flow rows). */ protected runToJson(run: JsonRunSource): JsonRunResult; /** * Builds the machine-readable document emitted by --json / --json-file. * Safe to call for every outcome, including errors raised before an app id * was assigned. */ toJsonOutput(result: ProviderResult): JsonOutput; protected credentials: Credentials; protected options: TOptions; protected upload: Upload; protected spinner: Spinner; protected appId: number | undefined; protected activeRunIds: number[]; protected isShuttingDown: boolean; protected signalHandler: (() => void) | null; private tunnelInstance; /** * The base URL for the provider's API endpoint */ protected abstract readonly URL: string; constructor(credentials: Credentials, options: TOptions); /** * Ensures an output directory exists, creating it if necessary. */ protected ensureOutputDirectory(dirPath: string): Promise; /** * Sets up signal handlers for graceful shutdown (SIGINT, SIGTERM) */ protected setupSignalHandlers(): void; /** * Removes signal handlers */ protected removeSignalHandlers(): void; /** * Override hook for subclasses to stop any extra animation loops (e.g. * Maestro's flow-table timer) during shutdown. Default is a no-op. */ protected stopAnimations(): void; /** * Handles graceful shutdown when interrupt signal is received */ protected handleShutdown(): void; /** * Starts a TestingBot tunnel if the --tunnel option is enabled. */ protected startTunnel(): Promise; /** * Stops the TestingBot tunnel if one is running. */ protected stopTunnel(): Promise; /** * Stops all active test runs */ protected stopActiveRuns(): Promise; /** * Stops a specific test run */ protected stopRun(runId: number): Promise; /** * Clears the current line in the terminal */ protected clearLine(): void; /** * Sleeps for the specified number of milliseconds */ protected sleep(ms: number): Promise; /** * Maximum number of retries for transient errors */ protected readonly MAX_RETRIES = 3; /** * Base delay for exponential backoff (in milliseconds) */ protected readonly BASE_RETRY_DELAY_MS = 2000; /** * Executes an async operation with automatic retry for transient errors. * Uses exponential backoff between retries. * * @param operation - Description of the operation (for logging) * @param fn - Async function to execute * @returns The result of the function * @throws The last error if all retries fail */ protected withRetry(operation: string, fn: () => Promise): Promise; /** * Extracts an error message from various error types. * For Axios errors, uses enhanced error handling with diagnostics. */ protected extractErrorMessage(cause: unknown): string | null; /** * Checks internet connectivity and logs diagnostic information. * Useful when network errors occur to help users troubleshoot. */ protected checkAndReportConnectivity(): Promise; /** * Performs a quick connectivity check before starting operations. * Throws an error with diagnostics if no connection is available. */ protected ensureConnectivity(): Promise; /** * Handles errors with enhanced diagnostics. * For network errors, performs connectivity check. */ protected handleErrorWithDiagnostics(error: unknown, operation: string): Promise; /** * Formats elapsed time in human-readable format */ protected formatElapsedTime(seconds: number): string; /** * Prints dry-run summary showing what would be sent to the API. */ protected printDryRunSummary(sections: { provider: string; apiUrl: string; uploads: { label: string; filePath: string; endpoint: string; }[]; runPayload: Record; }): void; } //# sourceMappingURL=base_provider.d.ts.map