cumulocity-cypress
Version:
Cypress commands for Cumulocity IoT
1,311 lines (1,299 loc) • 58.9 kB
TypeScript
import { Request as Request$1, RequestHandler, Express } from 'express';
import winston from 'winston';
import { IncomingMessage, ServerResponse, ClientRequest, Server } from 'http';
import { IAuthentication, ICredentials, Client, IResult, IFetchResponse, IResultList, IFetchOptions } from '@c8y/client';
import debug from 'debug';
import { FormatFn } from 'morgan';
import { Options } from 'http-proxy-middleware';
import { Har } from 'har-format';
export { Content, Entry, Har, Header, PostData, QueryString } from 'har-format';
/**
* Interface for a preprocessor to apply transformations to `C8yPact` records
* before critical operations such as matching or saving. A preprocessor can be used to
* unify records by removing or obfuscating sensitive data, picking only certain keys to keep,
* or applying regex substitutions to values. Preprocessors operate in-place on the
* given object, which can be a full `C8yPact`, an individual `C8yPactRecord`, or a
* plain object such as `Cypress.Response`.
*
* The default implementation is `C8yDefaultPactPreprocessor`, which supports the
* operations and key-path syntax described in `C8yPactPreprocessorOptions`.
*
* See {@link C8yDefaultPactPreprocessor} for default implementation.
*
*/
interface C8yPactPreprocessor {
/**
* Configuration options used by the preprocessor.
*/
readonly options?: C8yPactPreprocessorOptions;
/**
* Applies the preprocessor options (rules) to the given object in-place.
*
* When `obj` is a `C8yPact` (i.e. contains a `records` array), all records
* are processed individually. For a plain object, including `Cypress.Response`
* or `C8yPactRecord`, the object itself is processed.
*
* Operations are applied in this sequence:
* 1. **pick** — remove any keys not listed
* 2. **obfuscate** — replace values with the obfuscation pattern
* 3. **regexReplace** — apply regex substitutions
* 4. **ignore** — delete values entirely
*
* @param obj Object to preprocess. Modified in place.
* @param options Options that override the instance-level options for this
* single call.
*/
apply: (obj: Partial<Cypress.Response<any> | C8yPactRecord | C8yPact>, options?: C8yPactPreprocessorOptions) => void;
}
/**
* Configuration options for `C8yPactPreprocessor`.
*
* All key-path strings support:
* - Dot-separated segments: `response.body.password`
* - Bracket / numeric-index notation: `response.body.items[0].token`
* - Array fan-out: `response.body.users.password` (applied to every element)
* - Recursive descent: `response.body..password` (any depth below `body`)
*
* Key resolution is case-insensitive when `ignoreCase` is `true` (default).
*/
interface C8yPactPreprocessorOptions {
/**
* Key paths whose values should be replaced with `obfuscationPattern`.
*
* For `Authorization` headers whose value begins with `Bearer` or `Basic`,
* the scheme prefix is kept and only the credential token is replaced, e.g.
* `Bearer ****`.
*
* Cookie values can be targeted by appending the cookie name as an extra
* segment: `request.headers.cookie.XSRF-TOKEN`.
*
* @example
* obfuscate: [
* "response.body.password",
* "request.headers.authorization",
* "response.body.users.password", // every user object
* "response.body..token", // any depth
* ]
*/
obfuscate?: string[];
/**
* Key paths whose values should be deleted entirely from the record.
*
* Cookie values can be targeted by appending the cookie name as an extra
* segment: `request.headers.cookie.XSRF-TOKEN`.
*
* @example
* ignore: [
* "request.headers.accept-encoding",
* "response.headers.cache-control",
* "response.body..internalDebugField", // any depth
* ]
*/
ignore?: string[];
/**
* Restricts which child keys are retained under the specified parent paths.
* All other sibling keys are removed.
*
* Two forms are accepted:
*
* **Object form** — maps a parent key path to an array of child keys to keep:
* ```ts
* pick: {
* "response.headers": ["content-type", "location"],
* "request.headers": ["authorization"]
* }
* ```
*
* **Array form** — keeps only the listed top-level keys of the record:
* ```ts
* pick: ["request", "response"]
* ```
*/
pick?: {
[key: string]: string[];
} | string[];
/**
* Applies one or more regex substitutions to the value at the given key path.
*
* The key is a key path (same syntax as `obfuscate` / `ignore`). The value
* is a single regex string or an ordered array of regex strings applied in
* sequence. Each regex string must use the format:
* ```
* /pattern/replacement/flags
* ```
* Capture groups (`$1`, `$2`, …) and all standard JS regex flags are
* supported.
*
* @example
* // Redact all but the first four characters of a token:
* // key: "response.body.token"
* // value: "/^(.{4}).+$/$1----/"
* //
* // Normalise a date field, then strip milliseconds in sequence:
* // key: "response.body.timestamp"
* // value: ["/T\\d{2}:\\d{2}:\\d{2}/THH:MM:SS/", "/\\.\\d+Z$/Z/"]
*/
regexReplace?: {
[key: string]: string | string[];
};
/**
* Replacement string used by the `obfuscate` operation.
* Defaults to `"****"`.
*/
obfuscationPattern?: string;
/**
* When `true` (default), key segments are matched case-insensitively at
* every level of the path. The actual casing of the key found in the object
* is always used for mutations — the original structure is never changed.
*/
ignoreCase?: boolean;
}
/**
* Default options for `C8yPactPreprocessor`. Used when constructing an instance
* without custom options, and as fallback for missing properties when applying
* with partial options.
*/
declare const C8yPactPreprocessorDefaultOptions: {
ignore: string[];
obfuscate: string[];
obfuscationPattern: string;
ignoreCase: boolean;
};
/**
* Preprocessor for `C8yPact` objects. A preprocessor is applied when a pact
* record is **saved** (recording mode) and before it is applied (matched)
* against any other record. This is used to unify records before they are
* **matched**. A preprocessor transforms objects such as `Cypress.Response`,
* `C8yPactRecord` or a full `C8yPact` in-place. It supports various operations
* to remove or obfuscate sensitive data, or to pick only certain keys to keep.
*
* The default implementation is `C8yDefaultPactPreprocessor`.
*
* ### Supported operations (configured via `C8yPactPreprocessorOptions`)
*
* | Option | Effect |
* |---|---|
* | `ignore` | Removes the value at each key path |
* | `obfuscate` | Replaces the value at each key path with `obfuscationPattern` |
* | `pick` | Keeps only the specified child keys; removes all others |
* | `regexReplace` | Applies one or more `/pattern/replacement/flags` expressions |
*
* ### Key-path syntax
*
* All key paths use dot-separated segments. Bracket notation and numeric array
* indices are supported:
* ```
* response.body.password
* response.body.items[0].token
* response.body.items.0.token
* ```
* When a path segment resolves to an **array of objects** and the next segment
* is *not* a numeric index, the operation fans out to every element:
* ```
* response.body.users.password // applied to every object in `users`
* ```
*
* ### Recursive-descent operator (`..`)
*
* Prefix a leaf key with `..` to match it at **any depth** below the optional
* prefix path:
* ```
* ..password // `password` anywhere in the record
* response.body..password // `password` anywhere inside `body`
* ```
*
* ### Case-insensitive matching
*
* When `ignoreCase` is `true` (the default), each path segment is resolved
* without regard to capitalization. Mutations always use the actual key name
* found in the object.
*
* ### Cookie / Set-Cookie shorthand
*
* Preprocessors automatically parse `Cookie` and `Set-Cookie` header strings and
* apply obfuscation or ignoring to individual cookie values when the key path
* is appended with the cookie name as an extra segment:
*
* ```
* request.headers.cookie.XSRF-TOKEN
* response.headers.set-cookie.authorization
* ```
*
* ### Authorization-header obfuscation
*
* When obfuscating an `Authorization` header whose value starts with `Bearer`
* or `Basic`, the scheme prefix is preserved and only the credential is
* replaced:
* ```
* Bearer ********
* Basic ********
* ```
*/
declare class C8yDefaultPactPreprocessor implements C8yPactPreprocessor {
static defaultObfuscationPattern: string;
options?: C8yPactPreprocessorOptions;
protected reservedKeys: string[];
constructor(options?: C8yPactPreprocessorOptions);
/** {@inheritDoc C8yPactPreprocessor.apply} */
apply(obj: Partial<Cypress.Response<any> | C8yPactRecord | C8yPact>, options?: C8yPactPreprocessorOptions): void;
private filterObjectByKeepPaths;
private applyKeepArray;
private removeKey;
private removeSetCookie;
private removeCookie;
private filterValidKeys;
/**
* Unified key-path traversal. Calls `fn(parent, resolvedKey)` on every
* matching leaf. Handles recursive descent (`..leafKey` / `prefix..leafKey`)
* and regular dot-/bracket-separated paths including array indices.
*/
private traverseKeyPath;
/**
* Applies a list of regex-replace patterns to the value at the given key path.
*/
private applyRegexReplace;
/**
* Recursively walks `obj` (depth-first) and calls `fn` on every node whose
* key matches `leafKey` (case-sensitively, or case-insensitively when
* `ignoreCase` is true). Traverses into arrays and plain objects.
*/
private applyRecursive;
private obfuscateKey;
private obfuscateSetCookie;
private obfuscateCookie;
protected resolveOptions(options?: Partial<C8yPactPreprocessorOptions>): C8yPactPreprocessorOptions;
private hasKey;
private getCookieObject;
}
declare function parseRegexReplace(input: string): {
pattern: RegExp;
replacement: string;
};
declare function performRegexReplace(input: string | any, regexes: {
pattern: RegExp;
replacement: string;
}[] | {
pattern: RegExp;
replacement: string;
}): string | any;
interface C8yAuthOptions extends ICredentials {
sendImmediately?: boolean;
userAlias?: string;
type?: string;
xsrfToken?: string;
}
type C8yPactAuthObject = {
user: string;
userAlias?: string;
type?: string;
token?: string;
} | {
userAlias: string;
user?: string;
type?: string;
token?: string;
};
type C8yAuthentication = IAuthentication;
/**
* Matcher for matching objects against a schema. If the object does not match
* the schema an Error will be thrown.
*/
interface C8ySchemaMatcher {
/**
* Matches the given object against the given schema. Throws an error when
* schema does not match. Strict matching controls whether additional properties
* are allowed in the object.
*
* @param obj Object to match.
* @param schema Schema to match obj against.
* @param strictMatching If true, additional properties are not allowed.
*/
match(obj: any, schema: any, strictMatching?: boolean): boolean;
}
/**
* A C8ySchemaGenerator is used to generate json schemas from json objects.
*/
interface C8ySchemaGenerator {
/**
* Generates a json schema for the given object.
*
* @param obj The object to generate the schema for.
* @param options The options passed to the schema generator.
*/
generate: (obj: any, options?: any) => Promise<any>;
}
/**
* Tenant ID of a Cumulocity tenant.
* @example t123456
*/
type C8yTenant = string;
/**
* Base URL of a Cumulocity tenant.
* @example https://tenant.eu-latest.cumulocity.com
*/
type C8yBaseUrl = string;
/**
* Options used to configure c8yclient command.
*/
type C8yClientOptions = Partial<Cypress.Loggable> & Partial<Cypress.Timeoutable> & Partial<Pick<Cypress.Failable, "failOnStatusCode">> & Partial<{
auth: IAuthentication;
baseUrl: C8yBaseUrl;
client: Client;
preferBasicAuth: boolean;
skipClientAuthentication: boolean;
failOnPactValidation: boolean;
ignorePact: boolean;
schema: any;
matchSchemaAndObject: boolean;
record: C8yPactRecord;
schemaMatcher: C8ySchemaMatcher;
strictMatching: boolean;
/** Custom ID to identify this request in logs. If not provided, a unique ID will be generated. */
requestId: string;
}>;
/**
* Wrapper for Client to pass auth and options without extending Client.
* Using underscore to avoid name clashes with Client and misunderstandings reading the code.
*/
interface C8yClient {
_auth?: C8yAuthentication;
_options?: C8yClientOptions;
_client?: Client;
}
/**
* Converts the given object to a Cypress.Response.
* @param obj The object to convert.
* @param duration The duration of the request.
* @param fetchOptions The fetch options used for the request.
* @param url The URL of the request.
* @param schema The schema of the response.
*/
declare function toCypressResponse(obj: IFetchResponse | IResult<any> | IResultList<any> | C8yPactRecord | Partial<Response>, duration?: number, fetchOptions?: IFetchOptions, url?: RequestInfo | URL, schema?: any): Cypress.Response<any> | undefined;
/**
* Checks if the given object is a window.Response.
* @param obj The object to check.
*/
declare function isWindowFetchResponse(obj: any): obj is Response;
/**
* Checks if the given object is an IResult.
* @param obj The object to check.
*/
declare function isIResult(obj: any): obj is IResult<any>;
/**
* Checks if the given object is a CypressError.
* @param error The object to check.
* @returns True if the object is a CypressError, false otherwise.
*/
declare function isCypressError(error: any): boolean;
declare const C8yPactModeValues: readonly ["record", "recording", "apply", "forward", "disabled", "mock"];
/**
* The pact mode is used to determine the behavior of the recording and mocking capabilities.
* - `record`: Records the requests and responses and stores them in a pact file.
* - `apply`: Mocks or matches the requests and responses from the recorded pact file.
* - `disabled`: Disables the pact recording and mocking (same as undefined).
* - `forward`: Forward requests without recording or mocking (might not be supported in all contexts).
* - `recording` (deprecated): same as `record`, use `record` instead.
* - `mock`: Mock responses for requests. Same as `apply`, but `apply` could also mean matching the requests and responses depending on the context.
*/
type C8yPactMode = (typeof C8yPactModeValues)[number];
declare const C8yPactRecordingModeValues: readonly ["refresh", "append", "new", "replace"];
/**
* The pact recording mode is used to determine how or if requests and responses are recorded.
* - `refresh` (default): Recreates the pact file with the all requests and responses.
* - `append`: Appends the new requests and responses to the existing pact file.
* - `new`: Only creates a new pact file if no pact file exists. If pact file exists, only new requests and responses are added.
* - `replace`: Overwrites existing records of a pact with new request and response in the order of occurence. Other records are kept as is.
*/
type C8yPactRecordingMode = (typeof C8yPactRecordingModeValues)[number];
/**
* ID representing a pact object. Should be unique.
* @example api__get__permission_failure_tests
*/
type C8yPactID = string;
interface C8yPactRequestMatchingOptions {
ignoreUrlParameters?: string[];
baseUrl?: C8yBaseUrl;
}
interface C8yPactConfigOptions {
/**
* ID representing a pact object.
*/
id?: C8yPactID;
/**
* Use to enable additional logging.
*/
log?: boolean;
/**
* Information describing the producer of the pact. Includes name and version information
*/
producer?: {
name: string;
version?: string;
} | string;
/**
* Information describing the consumer of the pact. Includes name and version information
*/
consumer?: {
name: string;
version?: string;
} | string;
/**
* Tags describing the pact.
*/
tags?: string[];
/**
* Description of the pact.
*/
description?: string;
/**
* Use ignore to disable the pact for the current test case.
*/
ignore?: boolean;
/**
* Use failOnMissingPacts to disable failing the test if no pact or no next record
* is found for the current test case.
*/
failOnMissingPacts?: boolean;
/**
* Use strictMatching to enable strict matching of the pact records. If strict matching
* is enabled, all properties of the pact records must match and tests fail if a property
* is missing. Default is false.
*/
strictMatching?: boolean;
/**
* If strictMocking is enabled, an error will be thrown if no pact record matches the
* current request. If disabled, the request will be passed through to the configured baseUrl.
*/
strictMocking?: boolean;
/**
* Options to configure the C8yPactPreprocessor.
*
* The preprocessor runs on every pact record when it is saved (recording mode)
* and before it is matched against any other record. Use it to remove or obfuscate
* sensitive values before they reach the fixture file, or to sanitize
* response bodies before they are compared.
*
* **Key-path syntax**
*
* All option values that accept key paths use dot-separated segments:
* ```
* response.body.password
* request.headers.Authorization
* ```
* Bracket notation and numeric indices are also supported and the style is
* preserved in output:
* ```
* response.body.items[0].token // bracket style
* response.body.items.0.token // dot style
* ```
* When a segment resolves to an **array of objects** and the next segment is
* not a numeric index, the operation is applied to every element of that
* array automatically:
* ```
* response.body.users.password // obfuscates `password` in every user object
* ```
*
* **Recursive-descent operator (`..`)**
*
* Prefix the leaf key with `..` to apply the operation at any nesting depth
* below the current node (or below the optional prefix path):
* ```
* ..password // find `password` anywhere in the record
* response.body..password // find `password` anywhere inside body
* ```
*
* **Case-insensitive matching**
*
* Set `ignoreCase: true` (the default) to resolve keys without regard to
* capitalization. The resolved casing of the actual object key is used for
* all mutations, so the original structure is never corrupted.
*
* **Cookie / Set-Cookie shorthand**
*
* Append the cookie name as an extra segment to operate on a single cookie
* rather than the entire header string:
* ```
* request.headers.cookie.XSRF-TOKEN
* response.headers.set-cookie.authorization
* ```
*
* @example
* // Remove non-essential headers, obfuscate credentials, redact a token
* preprocessor: {
* ignore: ["response.headers.cache-control"],
* obfuscate: ["request.headers.authorization", "response.body..password"],
* regexReplace: {
* // keep only the first 4 chars, e.g. "abcd----"
* "response.body.token": "/^(.{4}).+$/$1----/"
* }
* }
*/
preprocessor?: C8yPactPreprocessorOptions;
/**
* Options to configure the C8yPact request matching.
*/
requestMatching?: C8yPactRequestMatchingOptions;
/**
* Recording mode for the pact. Default is `refresh`.
*/
recordingMode?: C8yPactRecordingMode;
/**
* When set to true, both schema validation and pact object matching are performed
* when a schema is provided to cy.c8yclient. By default only schema validation runs
* when a schema is provided. Default is false.
*/
matchSchemaAndObject?: boolean;
}
type C8yPactConfigKeys = keyof C8yPactConfigOptions;
interface C8yPactEnv {
tenant?: C8yTenant;
loggedInUser?: string;
loggedInUserAlias?: string;
testTitlePath?: string[];
systemVersion?: string;
pluginVersion?: string;
pluginFolder?: string;
}
interface C8yPactObject {
/**
* Pact records containing the requests and responses as well as auth information,
* configuration options and created objects.
*/
records: C8yPactRecord[];
/**
* Meta information describing the pact.
*/
info: C8yPactInfo;
/**
* Unique id of the pact.
*/
id: C8yPactID;
}
declare const C8yPactObjectKeys: string[];
/**
* Pact object. Contains all information about a recorded pact, including
* the pact records with requests and responses as well as info object with
* meta data. A C8yPact objtect must have an unique id.
*/
interface C8yPact extends C8yPactObject {
/**
* Clears all records of the pact. Also resets all indexes internally used for
* iterating over the records.
*/
clearRecords(): void;
/**
* Appends a new record to the pact. If skipIfExists is true, the record is
* only appended if no record with the same request exists.
* @param record The record to add.
* @param skipIfExists If true, the record is only appended if no record for the same request exists.
* @returns True if the record was appended, false otherwise.
*/
appendRecord(record: C8yPactRecord, skipIfExists?: boolean): boolean;
/**
* Replaces an existing record with a new record. If no record with the same
* request exists, the record is appended.
* @param record The record to be replaced.
* @returns True if the record was replaced, false otherwise.
*/
replaceRecord(record: C8yPactRecord): boolean;
/**
* Returns the next pact record or null if no more records are available.
* If an id is provided, the record is looked up by requestId or record id
* and the cursor is advanced to the position after the matched record.
* If no id is provided, the next record by sequential index is returned.
*/
nextRecord(id?: string): C8yPactRecord | null;
/**
* Returns the next pact record matching the given request. Request matching is
* based ob criteria like url and method. Returns null if no record is found.
*/
nextRecordMatchingRequest(request: Partial<Request> | {
url: string;
method: string;
}, baseUrl?: C8yBaseUrl): C8yPactRecord | null;
/**
* Returns an iterator for the pact records.
*/
[Symbol.iterator](): Iterator<C8yPactRecord | null>;
}
interface C8yPactInfoVersion {
system?: string;
c8ypact?: string;
runner?: string;
}
/**
* Meta information describing a pact and how it was recorded.
*/
interface C8yPactInfo extends C8yPactConfigOptions {
id: C8yPactID;
/**
* Version information of the system, runner and pact standard used to record the pact.
*/
version?: {
system?: string;
shell?: string;
shellName?: string;
c8ypact?: string;
runner?: string;
};
/**
* Title of the pact. Title is an array of suite and test titles.
*/
title?: string[];
/**
* Base URL when recording the pact.
*/
baseUrl: C8yBaseUrl;
/**
* Tenant when recording the pact.
*/
tenant?: C8yTenant;
}
/**
* The request stored in a C8yPactRecord.
*/
type C8yPactRequest = Partial<Cypress.RequestOptions> & {
$body?: any;
};
/**
* The response stored in a C8yPactRecord.
*/
interface C8yPactResponse<T> {
allRequestResponses?: any[];
body?: T;
duration?: number;
headers?: {
[key: string]: string | string[];
};
isOkStatusCode?: boolean;
status?: number;
statusText?: string;
method?: string;
$body?: any;
}
/**
* The C8yPactRecord contains all information about a recorded request. It contains
* the request and response as well as configuration options, auth information and
* the created object id.
*/
interface C8yPactRecord {
/**
* Unique id of the record. Optional.
*/
id?: C8yPactID;
/**
* Request of the record.
*/
request: C8yPactRequest;
/**
* Response of the record.
*/
response: C8yPactResponse<any>;
/**
* Modified response returned by interception RouteHandler.
*/
modifiedResponse?: C8yPactResponse<any>;
/**
* Configuration options used for the request.
*/
options?: C8yClientOptions;
/**
* Auth information used for the request. Can be Basic or Cookie auth. Contains username and possibly alias.
*/
auth?: C8yPactAuthObject;
/**
* Id of an object created by the request. Used for mapping when running the recording.
*/
createdObject?: string;
/**
* Converts the C8yPactRecord to a Cypress.Response object.
*/
toCypressResponse(): Cypress.Response<any>;
/**
* Returns the date of the response.
*/
date(): Date | null;
/**
* Returns if the record has a request header with the given key. Comparison is case-insensitive.
*/
hasRequestHeader(key: string): boolean;
/**
* Returns the auth type of the record. Currently supports `BasicAuth`, `CookieAuth` or undefined.
*/
authType(): "BasicAuth" | "CookieAuth" | "BearerAuth" | undefined;
}
declare function isValidPactId(value: string): boolean;
/**
* Creates an C8yPactID for a given string or array of strings.
* @param value The string or array of strings to convert to a pact id.
* @returns The pact id.
*/
declare function pactId(value: string | string[]): C8yPactID | undefined;
/**
* Validate the given pact mode. Throws an error if the mode is not supported
* or undefined.
* @param mode The pact mode to validate.
*/
declare function validatePactMode(mode?: string): void;
/**
* Validate the given pact recording mode. Throws an error if the mode is not supported
* or undefined.
* @param mode The pact recording mode to validate.
*/
declare function validatePactRecordingMode(mode?: string): void;
/**
* Checks if the given object is a C8yPact. This also includes checking
* all records to be valid C8yPactRecord instances.
*
* @param obj The object to check.
* @returns True if the object is a C8yPact, false otherwise.
*/
declare function isPact(obj: any): obj is C8yPact;
/**
* Checks if the given object is a C8yPactRecord.
*
* @param obj The object to check.
* @returns True if the object is a C8yPactRecord, false otherwise.
*/
declare function isPactRecord(obj: any): obj is C8yPactRecord;
/**
* Checks if the given object is a Cypress.Response.
*
* @param obj The object to check.
* @returns True if the object is a Cypress.Response, false otherwise.
*/
declare function isCypressResponse(obj: any): obj is Cypress.Response<any>;
/**
* Checks if the given object is a C8yPactError. A C8yPactError is an error
* with the name "C8yPactError".
*
* @param error The object to check.
* @returns True if the object is a C8yPactError, false otherwise.
*/
declare function isPactError(error: any): boolean;
/**
* Converts a Cypress.Response to a C8yPactRequest.
*/
declare function toPactRequest(response: Cypress.Response<any> | Partial<Cypress.Response<any>>): C8yPactRequest | undefined;
/**
* Converts a Cypress.Response to a C8yPactResponse.
*/
declare function toPactResponse<T>(response: Cypress.Response<T> | Partial<Cypress.Response<T>>): C8yPactResponse<T> | undefined;
type C8yPactSaveKeys = "id" | "info" | "records";
/**
* Returns the value of the environment variable with the given name. The function
* tries to find the value in the global `process.env` or `Cypress.env()`. If `env`
* is provided, the function uses the given object as environment.
*
* The function tries to find the value in the following order:
* - `name`
* - `camelCase(name)`
* - `CYPRESS_name`
* - `name.replace(/^C8Y_/i, "")`
* - `CYPRESS_camelCase(name)`
* - `CYPRESS_camelCase(name.replace(/^C8Y_/i, ""))`
*
* @param name The name of the environment variable.
* @param env The environment object to use. Default is `process.env` or `Cypress.env()`
*
* @returns The value of the environment variable or `undefined` if not found.
*/
declare function getEnvVar(name: string, env?: {
[key: string]: string;
}): string | undefined;
declare function isOneOfStrings(value: string, values: string[]): boolean;
declare function getCreatedObjectId(response: Cypress.Response<any> | Partial<Cypress.Response<any>> | C8yPactResponse<any>): string | undefined;
/**
* Default implementation of C8yPact. Use C8yDefaultPact.from to create a C8yPact from
* a Cypress.Response object, a serialized pact as string or an object implementing the
* C8yPact interface. Note, objects implementing the C8yPact interface may not provide
* all required functions and properties.
*/
declare class C8yDefaultPact implements C8yPact {
records: C8yPactRecord[];
info: C8yPactInfo;
id: C8yPactID;
protected recordIndex: number;
protected iteratorIndex: number;
protected requestIndexMap: {
[key: string]: number;
};
static strictMatching: boolean;
constructor(records: C8yPactRecord[], info: C8yPactInfo, id: C8yPactID);
/**
* Creates a C8yPact from a Cypress.Response object, a serialized pact as string
* or an object containing the pact records and info object. Throws an error if
* the input can not be converted to a C8yPact.
* @param obj The Cypress.Response, string or object to create a pact from.
* @param info The C8yPactInfo object containing additional information for the pact.
* @param client The optional C8yClient for options and auth information.
*/
static from(...args: [obj: Cypress.Response<any>, info: C8yPactInfo, client?: C8yClient] | [obj: string | C8yPact | C8yPactObject]): C8yDefaultPact;
clearRecords(): void;
appendRecord(record: C8yPactRecord, skipIfExists?: boolean): boolean;
replaceRecord(record: C8yPactRecord): boolean;
/**
* Returns the next pact record or null if no more records are available.
* If an id is provided, the record is looked up by requestId or record id
* and the cursor is advanced to the position after the matched record.
* If no id is provided, the next record by sequential index is returned.
*/
nextRecord(id?: string): C8yPactRecord | null;
currentRecordIndex(): number;
nextRecordMatchingRequest(request: Partial<Request> | {
url: string;
method: string;
}, baseUrl?: C8yBaseUrl): C8yPactRecord | null;
protected getIndexForKey(key: string): number;
protected setIndexForKey(key: string, index: number): void;
protected indexMapKey(request: Partial<Request> | C8yPactRequest, baseUrl?: C8yBaseUrl): string | undefined;
protected normalizeUrl(url: string | URL, parametersToRemove?: string[], baseUrl?: C8yBaseUrl): string;
protected matchUrls(url1: string | URL, url2: string | URL, baseUrl?: C8yBaseUrl): boolean;
protected getRequesIndex(key: string): number;
/**
* Returns the pact record for the given request or null if no record is found.
* Currently only url and method are used for matching.
* @param req The request to use for matching.
*/
getRecordsMatchingRequest(req: Partial<Request> | C8yPactRequest, baseUrl?: C8yBaseUrl): C8yPactRecord[] | null;
/**
* Returns an iterator for the pact records to iterate records using `for (const record of pact) {...}`.
*/
[Symbol.iterator](): Iterator<C8yPactRecord | null>;
}
type C8yPactSerializeOptions = {
preprocessor?: C8yPactPreprocessor;
client?: C8yClient;
modifiedResponse?: Cypress.Response<any>;
loggedInUser?: string;
loggedInUserAlias?: string;
authType?: string;
baseUrl?: C8yBaseUrl;
};
declare function toSerializablePactRecord(response: Partial<Cypress.Response<any>>, options?: C8yPactSerializeOptions): C8yPactRecord;
declare function toPactSerializableObject(response: Partial<Cypress.Response<any>>, info: C8yPactInfo, options?: C8yPactSerializeOptions): Promise<Pick<C8yPact, C8yPactSaveKeys>>;
/**
* Constructor parameters for C8yDefaultPactRecord
*/
interface C8yDefaultPactRecordInit {
request: C8yPactRequest;
response: C8yPactResponse<any>;
options?: C8yClientOptions;
auth?: C8yPactAuthObject;
createdObject?: string;
modifiedResponse?: C8yPactResponse<any>;
id?: C8yPactID;
}
/**
* Default implementation of C8yPactRecord. Use C8yDefaultPactRecord.from to create
* a C8yPactRecord from a Cypress.Response object or an C8yPactRecord object.
*/
declare class C8yDefaultPactRecord implements C8yPactRecord {
id?: C8yPactID;
request: C8yPactRequest;
response: C8yPactResponse<any>;
options?: C8yClientOptions;
auth?: C8yPactAuthObject;
createdObject?: string;
modifiedResponse?: C8yPactResponse<any>;
constructor(request: C8yPactRequest, response: C8yPactResponse<any>, options?: C8yClientOptions, auth?: C8yPactAuthObject, createdObject?: string, modifiedResponse?: C8yPactResponse<any>, id?: C8yPactID);
constructor(params: C8yDefaultPactRecordInit);
/**
* Creates a C8yPactRecord from a Cypress.Response or an C8yPactRecord object.
* @param obj The Cypress.Response<any> or C8yPactRecord object.
* @param auth The auth information to use.
* @param client The C8yClient for options and auth information.
* @param id The optional ID for the pact record.
*/
static from(obj: Cypress.Response<any> | C8yPactRecord | Partial<Cypress.Response<any>>, auth?: C8yAuthOptions, client?: C8yClient, id?: C8yPactID): C8yPactRecord;
/**
* Returns the date of the response.
*/
date(): Date | null;
/**
* Converts the C8yPactRecord to a Cypress.Response object.
*/
toCypressResponse<T>(): Cypress.Response<T>;
hasRequestHeader(key: string): boolean;
authType(): "BearerAuth" | "CookieAuth" | "BasicAuth" | undefined;
}
declare function createPactRecord(response: Partial<Cypress.Response<any>>, client?: C8yClient, options?: {
loggedInUser?: string;
loggedInUserAlias?: string;
authType?: string;
id?: C8yPactID;
}): C8yPactRecord;
/**
* Matcher for C8yPactRecord objects. Use C8yPactMatcher to match any two
* records. Depending on the matcher implementation an Error will be thrown
* or boolean is returned.
*/
interface C8yPactMatcher {
/**
* Matches objectToMatch against objectPact. Returns false if objectToMatch
* does not match objectPact or throws an error with details on failing match.
*
* @param obj1 Object to match.
* @param obj2 Pact to match obj1 against.
* @param {C8yPactMatcherOptions} options The C8yPactMatcherOptions to use for matching.
*/
match: (objectToMatch: any, objectPact: any, options?: C8yPactMatcherOptions) => boolean;
}
/**
* Error thrown when a C8yPactMatcher fails to match two objects.
* Contains the actual and expected values, the key that failed to match and
* the key path of the property that failed to match.
* The key path is a string representation of the path to the property that failed to match.
* For example: "body > id" for a property "id" in the "body" object.
* This error is used to provide detailed information about the match failure.
*/
declare class C8yPactMatchError extends Error {
actual: any;
expected: any;
key?: string;
keyPath?: string;
schema?: any;
constructor(message: string, options: {
actual: any;
expected: any;
key?: string;
keyPath?: string;
schema?: any;
});
}
interface C8yPactMatcherOptions {
strictMatching?: boolean;
matchSchemaAndObject?: boolean;
loggerProps?: {
[key: string]: any;
};
schemaMatcher?: C8ySchemaMatcher;
parents?: (string | number)[];
ignoreCase?: boolean;
ignorePrimitiveArrayOrder?: boolean;
requestId?: string;
}
/**
* Default implementation of C8yPactMatcher to match C8yPactRecord objects. Pacts
* are matched by comparing the properties of the objects using property matchers.
* If no property matcher is configured for a property, the property will be matched
* by equality. Disable Cypress.c8ypact.config.strictMatching to ignore properties that are
* missing in matched objects. In case objects do not match an C8yPactError is thrown.
*/
declare class C8yDefaultPactMatcher implements C8yPactMatcher {
propertyMatchers: {
[key: string]: C8yPactMatcher;
};
static schemaMatcher: C8ySchemaMatcher;
static options?: C8yPactMatcherOptions;
options?: C8yPactMatcherOptions;
/**
* Standard JSON Schema keywords that start with $ but are not schema matcher keys.
* These should be treated as regular object properties.
* @see https://json-schema.org/understanding-json-schema/reference
*/
private static readonly JSON_SCHEMA_KEYWORDS;
constructor(propertyMatchers?: {
[key: string]: C8yPactMatcher;
}, options?: C8yPactMatcherOptions);
match(obj1: any, obj2: any, options?: C8yPactMatcherOptions): boolean;
/**
* Check if a key is a schema matcher key (starts with $ but is not a standard JSON Schema keyword)
*/
private isSchemaMatcherKey;
private isKeyPathInObject;
/**
* Returns the property matcher for the given property name.
* @param key The property name to get the matcher for.
* @param ignoreCase Whether to ignore the case of the property name.
*/
getPropertyMatcher(key: string, ignoreCase?: boolean): any;
/**
* Adds a new property matcher for the given property name.
*/
addPropertyMatcher(propertyName: string, matcher: C8yPactMatcher): void;
/**
* Removes the property matcher for the given property name.
*/
removePropertyMatcher(propertyName: string): void;
}
/**
* Extends C8yDefaultPactMatcher with default property matchers for Cumulocity
* response bodies. It has rules configured at least for the following properties:
* id, statistics, lastUpdated, creationTime, next, self, password, owner, tenantId
* and lastPasswordChange. It is registered for the properties body and requestBody.
*/
declare class C8yPactBodyMatcher extends C8yDefaultPactMatcher {
constructor(propertyMatchers?: {});
}
declare class C8yIdentifierMatcher implements C8yPactMatcher {
match(obj1: any, obj2: any): boolean;
}
declare class C8yNumberMatcher implements C8yPactMatcher {
match(obj1: any, obj2: any): boolean;
}
declare class C8yStringMatcher implements C8yPactMatcher {
match(obj1: any, obj2: any): boolean;
}
declare class C8yIgnoreMatcher implements C8yPactMatcher {
match(): boolean;
}
declare class C8ySameTypeMatcher implements C8yPactMatcher {
match(obj1: any, obj2: any): boolean;
}
declare class C8yISODateStringMatcher {
match(obj1: any, obj2: any): boolean;
}
declare function oauthLogin(auth: C8yAuthOptions, baseUrl?: C8yBaseUrl): Promise<C8yAuthOptions>;
interface C8yPactAdapterOptions {
/** Enable loading of JavaScript pact files (.js, .cjs). Defaults to false. */
enableJavaScript?: boolean;
/** Optional id to use for example for logging purposes. */
id?: string;
}
/**
* Using C8yPactFileAdapter you can implement your own adapter to load and save pacts using any format you want.
* This allows loading pact objects from different sources, such as HAR files, pact.io, etc.
*
* The default adapter is C8yPactDefaultFileAdapter which loads and saves pact objects from/to
* json files using C8yPact objects. Default location is cypress/fixtures/c8ypact folder.
*
* Alternative adapters:
* - C8yPactHARFileAdapter: Reads/writes HAR (HTTP Archive) format for use with external tools
*/
interface C8yPactFileAdapter {
/**
* Loads all pact objects. The key must be the pact id used in C8yPact.id.
*/
loadPacts: () => {
[key: string]: C8yPactObject;
};
/**
* Loads a pact object by id from file.
*/
loadPact: (id: string) => C8yPactObject | null;
/**
* Saves a pact object.
*/
savePact: (pact: C8yPactObject) => void;
/**
* Deletes a pact object or file.
*/
deletePact: (id: string) => void;
/**
* Gets the folder where the pact files are stored.
*/
getFolder: () => string;
/**
* Checks if a pact exists for a given id.
*/
pactExists(id: string): boolean;
/**
* Provides some custom description of the adapter.
* @example C8yPactFileAdapter
*/
description(): string;
}
/**
* Default implementation of C8yPactFileAdapter which loads and saves C8yPact objects
* Provide location of the files using folder option. Default location is
* cypress/fixtures/c8ypact folder.
*
* This adapter supports loading of JSON and YAML pact files (.json, .yaml, .yml). When
* saviing pact files, it saves them as JSON files (.json).
*
* By using C8yPactAdapterOptions you can enable loading of JavaScript pact files (.js, .cjs).
* Use with caution, as this can lead to security issues if the files are not trusted.
*/
declare class C8yPactDefaultFileAdapter implements C8yPactFileAdapter {
folder: string;
protected enabledExtensions: string[];
protected fileExtension: string;
protected readonly id: string;
protected readonly log: debug.Debugger;
/**
* Creates an instance of C8yPactDefaultFileAdapter.
*
* @param folder - The folder where pact files are stored. Can be an absolute or relative path.
* @param options - Optional configuration for the adapter.
* @param options.enableJavaScript - If true, enables loading of JavaScript pact files (.js, .cjs). Defaults to false.
*/
constructor(folder: string, options?: C8yPactAdapterOptions);
description(): string;
getFolder(): string;
loadPacts(): {
[key: string]: C8yPactObject;
};
loadPact(id: string): C8yPactObject | null;
pactExists(id: string): boolean;
savePact(pact: C8yPactObject | Pick<C8yPactObject, C8yPactSaveKeys>): void;
deletePact(id: string): void;
readPactFiles(): string[];
/**
* @deprecated Use readPactFiles() instead.
*/
readJsonFiles(): string[];
protected deleteJsonFiles(): void;
protected loadPactObjects(): (C8yPactObject | null)[];
protected loadPactFromFile(filePath: string): C8yPactObject | null;
protected createFolderRecursive(f: string): string | undefined;
protected toAbsolutePath(f: string): string;
protected isNodeError<T extends new (...args: any) => Error>(error: any, type: T): error is InstanceType<T> & NodeJS.ErrnoException;
}
type LogFormat = "json" | "simple" | "combined" | "short" | "dev" | "tiny" | "common";
type C8yPactHttpResponse<T = any> = Pick<C8yPactResponse<T>, "status" | "statusText" | "body" | "headers">;
declare const C8yPactHttpControllerLogLevel: readonly ["info", "debug", "warn", "error"];
declare const C8yPactHttpControllerDefaultMode: C8yPactMode;
declare const C8yPactHttpControllerDefaultRecordingMode: C8yPactRecordingMode;
interface C8yPactHttpControllerOptions {
/**
* The resource path to use for the controller. Default is "/c8yctrl".
*/
resourcePath?: string;
/**
* Base URL of the target server to proxy requests to.
*/
baseUrl?: C8yBaseUrl;
/**
* Authentication options to use for authenticating against the target server.
*/
auth?: C8yAuthOptions;
/**
* Hostname or interface to listen on. Default is to listen on all interfaces.
* @example "localhost" might listen on IPv6 only depending on the system
* @example "127.0.0.1" for IPv4 localhost only
* @example "0.0.0.0" for all IPv4 interfaces
* @example "::" for all IPv6 interfaces
*/
hostname?: string;
/**
* Port to listen on. Default is 3000.
*/
port?: number;
/**
* Tenant id of the target server to proxy requests to.
*/
tenant?: C8yTenant;
/**
* Root folder for static files to serve.
*/
staticRoot?: string;
/**
* Adapter to use for loading and saving pact files.
*/
adapter: C8yPactFileAdapter;
/**
* Preprocessor to use for modifying requests and responses.
*/
preprocessor?: C8yPactPreprocessor;
/**
* Request matching options to use for matching requests to recorded responses.
*/
requestMatching?: C8yPactRequestMatchingOptions;
/**
* Enable strict mocking. If true, only recorded responses are returned.
*/
strictMocking?: boolean;
/**
* Mode the controller is running in.
*/
mode?: C8yPactMode;
/**
* Recording mode to use for recording requests and responses.
*/
recordingMode?: C8yPactRecordingMode;
/**
* Id of the pact to use for recording and mocking. Default is undefined.
*/
pactId?: C8yPactID;
/**
* Record to use for error responses when no mock is found.
*/
mockNotFoundResponse?: C8yPactHttpResponse | ((req: Request$1<any, any, any, any>) => C8yPactHttpResponse);
/**
* Logger to use for logging. Currently only winston is supported.
*/
logger?: winston.Logger;
/**
* RequestHandler to use for logging requests. Default is morgan.
*/
requestLogger?: RequestHandler[] | ((logger?: winston.Logger) => RequestHandler[]);
/**
* RequestHandler to use for logging errors. Default is morgan logger
* that logs error object with url, status, request and response details.
* Use "common", "combined", "dev", "short" or "tiny" to use predefined
* mporgan log formats.
*/
errorLogger?: RequestHandler | "common" | "combined" | "dev" | "short" | "tiny" | string;
/**
* Log level to use for logging. Default is info.
*/
logLevel?: (typeof C8yPactHttpControllerLogLevel)[number];
/**
* Log format to use for logging.
*/
logFormat?: LogFormat | string | FormatFn<IncomingMessage, ServerResponse<IncomingMessage>>;
/**
* The app names and version to use from static folder. E.g. "cockpit: 1020".
* The version must be a semver range.
* @example { "cockpit": ">1020.0.0", "dtm": "^1018.1.0" }
*/
appsVersions?: {
[key: string]: string;
};
/**
* Custom replacer function to use for JSON.stringify. Use for customization of JSON output.
* Default is to replace URLs in "self", "next", "initRequest" properties with hostname and
* port of the controller.
*/
stringifyReplacer?: (key: string, value: any) => any;
/**
* Callbacks for hooking into the controller lifecycle.
*/
on: C8yPactHttpControllerCallbackOptions;
/**
* Options to pass to the underlying http-proxy.
*/
proxyOptions?: Omit<Options, "on" | "plugins" | "ejectPlugins">;
}
interface C8yPactHttpControllerCallbackOptions {
/**
* Called before the controller is started.
* @param ctrl The controller instance.
* @param config The configuration used for the controller.
*/
beforeStart?: (ctrl: C8yPactHttpController, config?: C8yPactHttpControllerOptions) => void;
/**
* Called before a request is mocked. Use to modify or return custom response as mock. Also
* use to forward custom headers from the request to the response. By default, only the
* recorded `content-type` and `set-cookie` headers are forwarded. Any other recorded headers
* must be forwarded by adding them to the `response.headers` object.
*
* By returning null or undefined, the request is passed to the proxy handler without mocking.
*
* **Note**: return `record.response` to use the recorded response as mock.
*
* @param ctrl The controller instance.
* @param req The request to mock.
* @param record The record used for mocking.
* @returns A response to use as mock or null/undefined to pass the request to the proxy handler.
*/
mockRequest?: (ctrl: C8yPactHttpController, req: Request$1<any, any, any, any>, record: C8yPactRecord | undefined | null) => C8yPactHttpResponse | undefined | null;
/**
* Called when a request is not found in the recorded pacts. Use to return a custom response
* for the request or `undefined` if `mockNotFoundResponse` or default 404 response should
* be returned for the given request. To access the pact, the response was not found in, use
* `c8yctl.currentPact`.
* @param ctrl The controller instance.
* @param req The request to be mocked.
* @returns A response to use as mock for the request or `undefined` to use the default 404 response.
*/
mockNotFound?: (ctrl: C8yPactHttpController, req: Request$1) => C8yPactHttpResponse | undefined;
/**
* Called before a request is proxied. Use to modify the request before it
* is proxied, e.g. to add or remove headers, etc. or to abort the request
* by returning a custom or error response to send back to the client.
* @param ctrl The controller instance.
* @param proxyReq The proxy request.
* @param req The request to proxy.
* @returns A response to send back to the client to abort the request or