3.8.1 • Published 4 months ago

cypress-interceptor v3.8.1

Weekly downloads
-
License
MIT
Repository
github
Last release
4 months ago

NPM Build Status Coverage Status

Cypress Interceptor

Example of using

About

Cypress Interceptor is a substitute for cy.intercept. Its main purpose is to log all fetch or XHR requests, which can be analyzed in case of failure. It provides extended ways to log these statistics, including the ability to mock or throttle requests easily. Cypress Interceptor is better than cy.intercept because it can avoid issues, especially when using global request catch.

There is also an option to monitor the web browser console output and log it to a file or work with websockets. For more details, refer to the Watch The Console or websocket section.

Motivation

This diagnostic tool is born out of extensive firsthand experience tracking down elusive, seemingly random Cypress test failures. These issues often weren’t tied to Cypress itself, but rather to the behavior of the underlying web application—especially in headless runs on build servers where no manual interaction is possible. By offering robust logging for both API requests and the Web console, the tool provides greater transparency and insight into the root causes of failures, ultimately helping developers streamline their debugging process and ensure more reliable test outcomes.

What's new

  • Added test.unit as a helper for testing.
  • Improved the use of Interceptor in before hooks and added the ability to pass a function to cy.waitUntilRequestIsDone
  • Watch The Console has been reworked and its logic completely changed
  • The improved Watch The Console now safely logs objects and functions, with an added filtering option
  • Added cy.writeInterceptorStatsToLog and cy.wsInterceptorStatsToLog
  • Complete rework, exclude cy.intercept as the main tool of logging, stabilizing runs, support all fetch and XHR body types
  • Work with canceled and aborted requests
  • Add support for Websockets

Table of contents

Getting started

It is very simple, just install the package using yarn or npm and import the package in your cypress/support/e2e.js, cypress/support/e2e.ts or in any of your test files:

import "cypress-interceptor";

Would you just log all requests to a file on fail?

Take a look at this example.

Would you like to wait until a request or requests are done?

Refer to this section

The Cypress Interceptor commands

/**
 * Get an instance of Interceptor
 *
 * @returns An instance of Interceptor
 */
interceptor: () => Chainable<Interceptor>;
/**
 * Get the last call matching the provided route matcher.
 *
 * @param routeMatcher A route matcher
 * @returns The last call information or `undefined` if none matches.
 */
interceptorLastRequest: (
    routeMatcher?: IRouteMatcher
) => Chainable<CallStack | undefined>;
/**
 * Set the Interceptor options. This must be called before a request occurs.
 *
 * @param options Options
 * @returns The current Interceptor options
 */
interceptorOptions: (options?: InterceptorOptions) => Chainable<InterceptorOptions>;
/**
 * Get the number of requests matching the provided route matcher.
 *
 * @param routeMatcher A route matcher
 * @returns The number of requests matching the provided route matcher since the current test started.
 */
interceptorRequestCalls: (routeMatcher?: IRouteMatcher) => Chainable<number>;
/**
 * Get the statistics for all requests matching the provided route matcher since the beginning
 * of the current test.
 *
 * @param routeMatcher A route matcher
 * @returns It returns all requests matching the provided route matcher with detailed information.
 * If none match, it returns an empty array.
 */
interceptorStats: (routeMatcher?: IRouteMatcher) => Chainable<CallStack[]>;
/**
 * Mock the response of requests matching the provided route matcher. By default, it mocks the
 * first matching request, and then the mock is removed. Set `times` in the options to change
 * how many times the matching requests should be mocked.
 *
 * @param routeMatcher A route matcher
 * @param mock The response mock
 * @param options The mock options
 * @returns The ID of the created mock. This is needed if you want to remove the mock manually.
 */
mockInterceptorResponse(
    routeMatcher: IRouteMatcher,
    mock: IMockResponse,
    options?: IMockResponseOptions
): Chainable<number>;
/**
 * Reset the Interceptor's watch. It sets the pointer to the last call. Resetting the pointer
 * is necessary when you want to wait for certain requests.
 *
 * Example: On a site, there are multiple requests to api/getUser, but we want to wait for the
 * specific one that occurs after clicking a button. Since we cannot know which api/getUser call
 * to wait for, calling this method sets the exact point from which we want to check the next requests.
 */
resetInterceptorWatch: VoidFunction;
/**
 * Start the time measurement (a helper function)
 *
 * @returns performance.now() when the code is executed
 */
startTiming: () => Chainable<number>;
/**
 * Stop the time measurement (a helper function)
 *
 * @returns If `cy.startTiming` was called, it returns the time difference since startTiming was
 * called (in ms); otherwise, it returns `undefined`.
 */
stopTiming: () => Chainable<number | undefined>;
/**
 * Throttle requests matching the provided route matcher by setting a delay. By default, it throttles
 * the first matching request, and then the throttle is removed. Set times in the options to change
 * how many times the matching requests should be throttled.
 *
 * @param routeMatcher A route matcher
 * @param delay The delay in ms
 * @param options The throttle options (which can include mocking the response).
 * @returns The ID of the created throttle. This is needed if you want to remove the throttle manually.
 */
throttleInterceptorRequest(
    routeMatcher: IRouteMatcher,
    delay: number,
    options?: IThrottleRequestOptions
): Chainable<number>;
  /**
 * The method will wait until all requests matching the provided route matcher are finished or until
 * the maximum waiting time (`timeout` in options) is reached.
 *
 * By default, there must be at least one match. Otherwise, it waits until a request matches the
 * provided route matcher or until the maximum waiting time is reached. This behavior can be changed
 * by setting `enforceCheck` to `false` in the options.
 *
 * @param action An action which should trigger a request
 * @param stringMatcherOrOptions A string matcher OR options with a route matcher
 * @param errorMessage An error message when the maximum waiting time is reached
 * @returns The result from the action
 */
waitUntilRequestIsDone<T>(
    action: () => Cypress.Chainable<T>,
    stringMatcherOrOptions?: StringMatcher | WaitUntilRequestOptions,
    errorMessage?: string
): Chainable<T>;
/**
 * @param action An action which should trigger a request
 * @param stringMatcherOrOptions A string matcher OR options with a route matcher
 * @param errorMessage An error message when the maximum waiting time is reached
 * @returns The result from the action
 */
waitUntilRequestIsDone<T>(
    action: () => T,
    stringMatcherOrOptions?: StringMatcher | WaitUntilRequestOptions,
    errorMessage?: string
): Chainable<T>;
/**
 * @param stringMatcherOrOptions A string matcher OR options with a route matcher
 * @param errorMessage An error message when the maximum waiting time is reached
 * @returns An instance of the Interceptor
 */
waitUntilRequestIsDone(
    stringMatcherOrOptions?: StringMatcher | WaitUntilRequestOptions,
    errorMessage?: string
): Chainable<Interceptor>;
/**
 * Write the logged requests' information (or those filtered by the provided route matcher) to a file
 *
 * @example cy.writeInterceptorStatsToLog("./out") => the output file will be "./out/Description - It.stats.json"
 * @example cy.writeInterceptorStatsToLog("./out", { fileName: "file_name" }) =>  the output file will be "./out/file_name.stats.json"
 * @example cy.writeInterceptorStatsToLog("./out", { routeMatcher: { method: "GET" } }) => write only "GET" requests to the output file
 * @example cy.writeInterceptorStatsToLog("./out", { mapper: (callStack) => ({ type: callStack.type, url: callStack.url }) }) => map the output that will be written to the output file
 *
 * @param outputDir The path for the output folder
 * @param options Options
 */
writeInterceptorStatsToLog: (outputDir: string, options?: WriteStatsOptions & Partial<Cypress.WriteFileOptions & Cypress.Timeoutable>) => Chainable<null>;

Cypress environment variables

You can provide Cypress environment variables to set certain Interceptor options globally:

e2e: {
    env: {
        INTERCEPTOR_REQUEST_TIMEOUT: number; // default 10000
    }
}

INTERCEPTOR_REQUEST_TIMEOUT - the value (in ms) that defines how long the Interceptor will wait for pending requests when call cy.waitUntilRequestIsDone()

Documentation and examples

In almost all methods, there is a route matcher (IRouteMatcher) that can be a string, a RegExp (StringMatcher), or an object with multiple matching options. For more information about matching options, explore IRouteMatcherObject.

cy.interceptor

interceptor: () => Chainable<Interceptor>;

Get an instance of the Interceptor

Example

cy.interceptor().then(interceptor => {
    interceptor.resetWatch();
    interceptor.removeMock(1);
    interceptor.removeThrottle(1);
});

cy.interceptorLastRequest

interceptorLastRequest: (routeMatcher?: IRouteMatcher) => Chainable<CallStack | undefined>;

References:

Get the last call matching the provided route matcher. Similar to cy.interceptorStats.

Example

// get the last fetch request
cy.interceptorLastRequest({ resourceType: "fetch" }).then((stats) => {
    // stats can be `undefined`
});

cy.interceptorOptions

interceptorOptions: (options?: InterceptorOptions) => Chainable<InterceptorOptions>;

References:

Set the Interceptor options. It's best to call this at the beginning of the test, in before or beforeEach. The default options are:

ignoreCrossDomain: false

Example

// ignore the cross-domain requests
cy.interceptorOptions({
    ignoreCrossDomain: true
});

cy.interceptorRequestCalls

interceptorRequestCalls: (routeMatcher?: IRouteMatcher) => Chainable<number>;

References:

Get the number of requests matching the provided route matcher.

// There should be only one call logged to a URL ending with `/api/getOrder`
cy.interceptorRequestCalls("**/api/getOrder").should("eq", 1);
// there should be only 4 fetch requests
cy.interceptorRequestCalls({ resourceType: ["fetch"] }).should("eq", 4);

cy.interceptorStats

interceptorStats: (routeMatcher?: IRouteMatcher) => Chainable<CallStack[]>;

References:

Get the statistics for all requests matching the provided route matcher since the beginning of the current test.

Example

Note: It just serves as an example, but I do not recommend testing any of it except for the request/response query and body—in some cases. It should basically serve for logging/debugging.

cy.interceptorStats("**/getUser").then((stats) => {
    expect(stats.length).to.eq(1);
    expect(stats[0].crossDomain).to.be.false;
    expect(stats[0].duration).to.be.lt(1500);
    expect(stats[0].request.body).to.deep.eq({ id: 5 });
    expect(stats[0].request.headers["host"]).to.eq("my-page.org");
    expect(stats[0].request.query).to.deep.eq({
        ref: 987
    });
    expect(stats[0].request.method).to.eq("POST");
    expect(stats[0].resourceType).to.eq("fetch");
    expect(stats[0].response?.body).to.deep.eq({ userName: "HarryPotter" });
    expect(stats[0].response?.statusCode).to.eq(200);
    expect(stats[0].response?.statusMessage).to.eq("OK");
    expect(stats[0].url.pathname.endsWith("/getUser")).to.be.true;
});

cy.mockInterceptorResponse

mockInterceptorResponse(
    routeMatcher: IRouteMatcher,
    mock: IMockResponse,
    options?: IMockResponseOptions
): Chainable<number>;

References:

Mock the response of requests matching the provided route matcher. By default, it mocks the first matching request, and then the mock is removed. Set times in the options to change how many times the matching requests should be mocked.

Important

By default, the mocked request does not reach the network layer. Set allowHitTheNetwork to true if you want the request to reach the network layer

Examples

// return status 400 to all fetch requests, indefinitely
cy.mockInterceptorResponse(
    { resourceType: "fetch" },
    { statusCode: 400 },
    { times: Number.POSITIVE_INFINITY }
);
// return a custom body to a request ending with `/api/getUser`, default once
cy.mockInterceptorResponse(
    { url: "**/api/getUser" },
    { 
        body: {
            userName: "LordVoldemort"
        }
     }
);
// return a custom header to all POST requests, indefinitely
cy.mockInterceptorResponse(
    { method: "POST" },
    { 
        header: {
            "custom-header": "value"
        }
     },
     { times: Number.POSITIVE_INFINITY }
);
// return a custom body to any fetch request, twice
cy.mockInterceptorResponse(
    { resourceType: "fetch" },
    { 
        generateBody: (request, getJsonRequestBody) => {
            return {
                userName: "LordVoldemort"
            };
        }
     },
     { times: 2 }
);
// generate the response dynamically
cy.mockInterceptorResponse(
    { resourceType: "fetch" },
    {
        generateBody: (_, getJsonRequestBody) =>
            "page" in getJsonRequestBody<{ page: number }>()
            ? ({ custom: "resposne 1" })
            : ({ custom: "resposne 2" })
    }
);
// mock the request having query string `page` = 5, once
cy.mockInterceptorResponse(
    {
        queryMatcher: (query) => query?.page === 5
    },
    { 
        body: {
            userName: "LordVoldemort"
        }
    },
    {
        times: 1 // this is the default value, no need to set
    }
);
// mock the request having `page` in the request body, default once
cy.mockInterceptorResponse(
    {
        bodyMatcher: () => {
            try {
                const body = JSON.parse(bodyString);

                return isObject(body) && "page" in body;
            } catch {
                return false;
            }
        }
    },
    { 
        body: {
            userName: "LordVoldemort"
        }
    }
);

cy.resetInterceptorWatch

resetInterceptorWatch: () => void;

Reset the Interceptor's watch. It sets the pointer to the last call. Resetting the pointer is necessary when you want to wait for certain requests.

Example

On a site, there are multiple requests to api/getUser, but we want to wait for the specific one that occurs after clicking a button. Since we cannot know which api/getUser call to wait for, calling this method sets the exact point from which we want to check the next requests.

// this page contains multiple requests to `api/getUser` when visit
cy.visit("https://www.my-page.org");

// reset the watch, so all the previous (or pending) requests will be ignored in the next `waitUntilRequestIsDone`
cy.resetInterceptorWatch();

// this click should trigger a request to `/api/getUser`
cy.get("button#user-info").click();

// the test will not continue until the request to `/api/getUser` is finished
cy.waitUntilRequestIsDone("**/api/getUser");

cy.throttleInterceptorRequest

throttleInterceptorRequest(
    routeMatcher: IRouteMatcher,
    delay: number,
    options?: IThrottleRequestOptions
): Chainable<number>;

References:

Throttle requests matching the provided route matcher by setting a delay. By default, it throttles the first matching request, and then the throttle is removed. Set times in the options to change how many times the matching requests should be throttled.

In the options, the mockResponse property can accept the same mocking object as shown in cy.mockInterceptorResponse.

Example

// make the request to `/api/getUser` last for 5 seconds
cy.throttleInterceptorRequest("**/api/getUser", 5000);
// throttle a request which has the URL query string containing key `page` equal to 5
cy.throttleInterceptorRequest({ queryMatcher: (query) => query?.page === 5}, 5000);
// throtlle all requests for 5 seconds
cy.throttleInterceptorRequest({ resourceType: "all" }, 5000, { times: Number.POSITIVE_INFINITY });
cy.throttleInterceptorRequest("*", 5000, { times: Number.POSITIVE_INFINITY });
// throttle the request having `userName` in the request body
cy.throttleInterceptorRequest(
    {
        bodyMatcher:  (bodyString) => {
            try {
                const body = JSON.parse(bodyString);

                return isObject(body) && "userName" in body;
            } catch {
                return false;
            }
        }
    },
    5000
);

cy.waitUntilRequestIsDone

Example of using

/**
 * @param action An action which should trigger a request
 * @param stringMatcherOrOptions A string matcher OR options with a route matcher
 * @param errorMessage An error message when the maximum waiting time is reached
 * @returns The result from the action
 */
public waitUntilRequestIsDone<T>(
    action: () => Cypress.Chainable<T>,
    stringMatcherOrOptions?: StringMatcher | WaitUntilRequestOptions,
    errorMessage?: string
): Cypress.Chainable<T>;
/**
 * @param action An action which should trigger a request
 * @param stringMatcherOrOptions A string matcher OR options with a route matcher
 * @param errorMessage An error message when the maximum waiting time is reached
 * @returns The result from the action
 */
public waitUntilRequestIsDone<T>(
    action: () => T,
    stringMatcherOrOptions?: StringMatcher | WaitUntilRequestOptions,
    errorMessage?: string
): Cypress.Chainable<T>;
/**
 * @param stringMatcherOrOptions A string matcher OR options with a route matcher
 * @param errorMessage An error message when the maximum waiting time is reached
 * @returns An instance of the Interceptor
 */
public waitUntilRequestIsDone(
    stringMatcherOrOptions?: StringMatcher | WaitUntilRequestOptions,
    errorMessage?: string
): Cypress.Chainable<Interceptor>;

References:

The method will wait until all requests matching the provided route matcher are finished or until the maximum waiting time (timeout in options) is reached.

The timeout option is set to 10 seconds by default. This option can be set globally by Cypress environment variable INTERCEPTOR_REQUEST_TIMEOUT.

The timeout priority resolution.

const DEFAULT_TIMEOUT = 10000;
const timeout = option?.timeout ?? Cypress.env("INTERCEPTOR_REQUEST_TIMEOUT") ?? DEFAULT_TIMEOUT;

By default, there must be at least one match. Otherwise, it waits until a request matches the provided route matcher or until the maximum waiting time is reached. This behavior can be changed by setting enforceCheck to false in the options.

Important

It is crutial to call cy.resetInterceptorWatch() before an action that should trigger a request you want to wait for, or pass an action that should trigger the request as the first argument. The reason is that there may be a chain of requests preventing the one you want to wait for from being processed. More details below.

Examples

// will wait until all requests in progress are finished
cy.waitUntilRequestIsDone();
// will wait until the login request is finished
// cy.resetInterceptorWatch is called automatically when you provide a function as the first argument
cy.waitUntilRequestIsDone(
    // the request which triggers the request
    () => cy.contains("button", "Log In").click(),
    "**/api/log-in"
);
// when you do not provide a function as the first argument it is needed to call cy.resetInterceptorWatch
// because the request you want to wait for could be called before and Interceptor will ses it as done
cy.resetInterceptorWatch();
// any action that should trigger the request
cy.contains("button", "Log In").click();
// wait for requests ending with `/api/getUser`
cy.waitUntilRequestIsDone("**/api/getUser");
cy.waitUntilRequestIsDone(new RegExp("api\/getUser$", "i"));
cy.waitUntilRequestIsDone();
// any action that should trigger the request
action();
// wait for requests containing `/api/`
cy.waitUntilRequestIsDone("**/api/**");
cy.waitUntilRequestIsDone(new RegExp("(.*)\/api\/(.*)", "i"));
// wait until this request is finished
cy.waitUntilRequestIsDone("http://my-page.org/api/getUser");
// providing a custom error message when maximum time of waiting is reached
cy.waitUntilRequestIsDone("http://my-page.org/api/getUser", "Request never happened");
// wait until all fetch requests are finished
cy.waitUntilRequestIsDone({ resourceType: "fetch" });
// wait maximum 200s for this fetch to finish
cy.waitUntilRequestIsDone({ url: "http://my-page.org/api/getUser", timeout: 200000 });
// wait 2s after the request to `api/getUser` finishes to check if there is an another request
cy.waitUntilRequestIsDone({ url: "http://my-page.org/api/getUser", waitForNextRequest: 2000 });
// wait until all cross-domain requests are finished but do not fail if there is none
cy.waitUntilRequestIsDone({ crossDomain: true, enforceCheck: false });
// increase the timeout of `cy.writeFile` if you expect the stats to be large
cy.waitUntilRequestIsDone(outputDir, {
    timeout: 60000
});

cy.writeInterceptorStatsToLog

writeInterceptorStatsToLog(outputDir: string, options?: WriteStatsOptions & Partial<Cypress.WriteFileOptions & Cypress.Timeoutable>): Chainable<null>

References:

Write the logged requests' information (or those filtered by the provided route matcher) to a file. The file will contain the JSON.stringify of callStack.

Example

afterAll(() => {
    // the output file will be "./out/test.cy.ts (Description - It).stats.json" (the name of the file `test.cy.ts (Description - It)` will be generated from the running test)
    cy.writeInterceptorStatsToLog("./out");
    // increase the timeout for `cy.writeFile` when you expect a big output
    cy.writeInterceptorStatsToLog("./out", { timeout: 120000 });
    // the output file will be "./out/file_name.stats.json"
    cy.writeInterceptorStatsToLog("./out", { fileName: "file_name" });
    // write only "fetch" requests to the output file
    cy.writeInterceptorStatsToLog("./out", { routeMatcher: { resourceType: "fetch" }});
    // write only "POST" requests to the output file
    cy.writeInterceptorStatsToLog("./out", { filter: (entry) => entry.method === "POST" });
    // map the output that will be written to the output file
    cy.writeInterceptorStatsToLog("./out", { mapper: (entry) => ({ url: entry.url }) });
});

Interceptor public methods

callStack

get callStack(): CallStack[];

Return a copy of all logged requests since the Interceptor has been created (the Interceptor is created in beforeEach).

getLastRequest

Same as cy.interceptorLastRequest.

getStats

Same as cy.interceptorStats.

requestCalls

Same as cy.interceptorRequestCalls.

mockResponse

Same as cy.mockInterceptorResponse.

onRequestError

Function called when a request is cancelled, aborted or fails.

onRequestError(func: OnRequestError);

removeMock

removeMock(id: number): boolean;

Remove the mock entry by ID.

removeThrottle

removeThrottle(id: number): boolean;

Remove the throttle entry by ID.

resetWatch

Same as cy.resetInterceptorWatch.

setOptions

Same as cy.interceptorOptions.

throttleRequest

Same as cy.throttleInterceptorRequest.

waitUntilRequestIsDone

Same as cy.waitUntilRequestIsDone.

writeStatsToLog

Same as cy.writeInterceptorStatsToLog.

Interfaces

IHeadersNormalized

type IHeadersNormalized = { [key: string]: string };

InterceptorOptions

interface InterceptorOptions {
    /**
     * Ignore requests outside the domain (default: `false`)
     */
    ignoreCrossDomain?: boolean;
}

IMockResponse

interface IMockResponse {
    /**
     * When this property is set to `true`, it allows the request to reach the network.
     * By default, the mocked request does not reach the network layer.
     */
    allowHitTheNetwork?: boolean;
    /**
     * The response body, it can be anything
     */
    body?: unknown;
    /**
     * Generate a body with the original response body. This option has higher priority
     * than the `body` option.
     *
     * @param request An object with the request data (body, query, method, ...)
     * @param getJsonRequestBody It will try to return a parsed request body
     * @returns The response body, it can be anything
     */
    generateBody?: (request: IRequest, getJsonRequestBody: <T = unknown>() => T) => unknown;
    /**
     * If provided, this will be added to the original response headers.
     */
    headers?: IHeadersNormalized;
    /**
     * The response status code
     */
    statusCode?: number;
    /**
     * The response status text
     */
    statusText?: string;
}

IMockResponseOptions

interface IMockResponseOptions {
    /**
     * The number of times the response should be mocked. By default, it is set to 1.
     * Set it to Number.POSITIVE_INFINITY to mock the response indefinitely.
     */
    times?: number;
}

IRouteMatcher

/**
 * String comparison is case-insensitive. Provide a RegExp without the case-sensitive flag if needed.
 */
type IRouteMatcher = StringMatcher | IRouteMatcherObject;

IRouteMatcherObject

type IRouteMatcherObject = {
    /**
     * A matcher for the request body
     *
     * @param requestBody The request body in string format
     * @returns `true` if matches
     */
    bodyMatcher?: (requestBody: string) => boolean;
    /**
     * If set to `true`, only cross-domain requests will match
     */
    crossDomain?: boolean;
    /**
     * A matcher for the request headers
     *
     * @param requestHeaders The request headers
     * @returns `true` if matches
     */
    headersMatcher?: (requestHeaders: IHeaders) => boolean;
    /**
     * If set to `true`, only HTTPS requests will match
     */
    https?: RouteMatcherOptions["https"];
    /**
     * The request method (GET, POST, ...)
     */
    method?: RequestMethod;
    /**
     * A matcher for the query string (URL search params)
     *
     * @param query The URL qearch params as an object
     * @returns `true` if matches
     */
    queryMatcher?: (query: Record<string, string | number>) => boolean;
    /**
     * The resource type
     */
    resourceType?: IResourceType | IResourceType[] | "all";
    /**
     * A URL matcher, use * or ** to match any word in string
     *
     * @example "**\/api/call" will match "http://any.com/api/call", "http://any.com/test/api/call", "http://any.com/test/api/call?page=99", ...
     * @example "*\api\*" will match "http://any.com/api/call", "http://any.com/api/list", "http://any.com/api/call-2?page=99&filter=1",
     * @example "**" will match any URL
     */
    url?: StringMatcher;
};

IThrottleRequestOptions

interface IThrottleRequestOptions {
    /**
     * Mock a response for the provided route matcher. If used together with `mockResponse`
     * or `cy.mockInterceptorResponse`, it has lower priority.
     */
    mockResponse?: IMockResponse;
    /**
     * The number of times the request should be throttled. By default, it is set to 1.
     * Set it to Number.POSITIVE_INFINITY to throttle the request indefinitely.
     */
    times?: number;
}

StringMatcher

type StringMatcher = string | RegExp;

WaitUntilRequestOptions

interface WaitUntilRequestOptions extends IRouteMatcherObject {
    /**
     * The value is `true` by default. If set to `true`, a request matching the provided
     * route matcher must be logged by the Interceptor; otherwise, it waits until the
     * URL is logged and finished or fails if the waiting time runs out. If set to `false`,
     * it checks for a request matching the provided route matcher. If one exists, it
     * waits until the request is complete. If not, it does not fail and ends successfully.
     */
    enforceCheck?: boolean;
    /**
     * The duration Interceptor will wait for pending requests. The default is set to 10,000
     * or the value of the `INTERCEPTOR_REQUEST_TIMEOUT` environment variable if specified.
     */
    timeout?: number;
    /**
     * Time to wait in milliseconds. The default is set to 750.
     *
     * It is necessary to wait if there might be a following request after the last one
     * (due to JavaScript code and subsequent requests). Set it to 0 to skip repeated
     * checking for requests.
     */
    waitForNextRequest?: number;
}

WriteStatsOptions

interface WriteStatsOptions {
    /**
     * The name of the file. If `undefined`, it will be generated from the running test.
     */
    fileName?: string;
    /**
     * An option to filter the logged items
     *
     * @param callStack Call information stored in the stack
     * @returns `false` if the item should be skipped
     */
    filter?: (callStack: CallStack) => boolean;
    /**
     * An option to map the logged items
     *
     * @param callStack Call information stored in the stack
     * @returns Any object you want to log
     */
    mapper?: (callStack: CallStack) => unknown;
    /**
     * When set to `true`, the output JSON will be formatted with tabs
     */
    prettyOutput?: boolean;
    /**
     * A route matcher
     */
    routeMatcher?: IRouteMatcher;
}

Useful tips

Log on fail

Just use this code in your cypress/support/e2e.ts or cypress/support/e2e.js:

import "cypress-interceptor";

afterEach(function () {
    if (this.currentTest?.state === "failed") {
        cy.writeInterceptorStatsToLog("./mochawesome-report/_interceptor");
    }
});

The code above will write all requests to the output file. However, you can use a route matcher to filter only the requests you want. For example:

// the output will contain only ajax requests
cy.writeInterceptorStatsToLog("./mochawesome-report/_interceptor", { url: "**/my-api" });

See the method you can use: cy.writeInterceptorStatsToLog.

Clean the videos for successful tests

If you have a reporter and send the report somewhere, you may want videos only for failed tests. If so, you can do it like this in your cypress/support/e2e.ts or cypress/support/e2e.js:

import { defineConfig } from 'cypress';
import * as fs from 'fs';

export default defineConfig({
    e2e: {
        setupNodeEvents(on, config) {
            // clean videos for successful tests
            on('after:run', results => {
                if (!('runs' in results)) {
                    return;
                }

                for (const run of results.runs) {
                    if (run.stats.failures === 0 && run.video && fs.existsSync(run.video)) {
                        fs.unlinkSync(run.video);
                    }
                }
            });
        }
    }
});

Watch The Console

Watch The Console is a helper function for logging the browser's console to a file. It provides a class which observes the web browser console output. This output is possible to log to a file.

Getting started

In your cypress/support/e2e.js, cypress/support/e2e.ts or in any of your test files:

import "cypress-interceptor/console";

The Cypress WatchTheConsole commands

 /**
 * Get an instance of the WatchTheConsole
 *
 * @returns An instance of the WatchTheConsole
 */
watchTheConsole: () => Chainable<WatchTheConsole>;
/**
 * Set the WatchTheConsole options. This must be called before a web page is visited.
 *
 * @param options Options
 * @returns The current WatchTheConsole options
 */
watchTheConsoleOptions: (
    options?: WatchTheConsoleOptions
) => Chainable<WatchTheConsoleOptions>;
/**
 * Write the logged console output to a file
 *
 * @example cy.writeConsoleLogToFile("./out") => the output file will be "./out/Description - It.stats.json"
 * @example cy.writeConsoleLogToFile("./out", { fileName: "file_name" }) =>  the output file will be "./out/file_name.stats.json"
 * @example cy.writeConsoleLogToFile("./out", { types: [ConsoleLogType.ConsoleError, ConsoleLogType.Error] }) => write only the
 * console errors and unhandled JavaScript errors to the output file
 * @example cy.writeConsoleLogToFile("./out", { filter: (type, ...args) => typeof args[0] === "string" && args[0].startsWith("Custom log:") }) =>
 * filter all console output to include only entries starting with "Custom log:"
 *
 * @param outputDir The path for the output folder
 * @param options Options
 */
writeConsoleLogToFile: (
    outputDir: string,
    options?: WriteLogOptions & Partial<Cypress.WriteFileOptions & Cypress.Timeoutable>
) => Chainable<null>;

Documentation and examples

cy.watchTheConsole

watchTheConsole: () => Chainable<WatchTheConsole>;

Get an instance of the WatchTheConsole

Example

cy.watchTheConsole().then(watchTheConsole => {
    expect(
        watchTheConsole.log.filter((entry) => entry.type === ConsoleLogType.ConsoleError).length
    ).to.eq(0);
});

cy.watchTheConsoleOptions

watchTheConsoleOptions: (
    options?: WatchTheConsoleOptions
) => Chainable<WatchTheConsoleOptions>;

References:

Set the WatchTheConsole options. This must be called before a web page is visited.

Example

beforeEach(() => {
    /**
     * My application is using `redux-logger` and provides an extended log of the Redux store. Therefore,
     * it is necessary to remove circular dependencies and, most importantly, capture the object at the
     * moment it is logged to prevent changes in the store over time.
     */
    cy.watchTheConsoleOptions({ cloneConsoleArguments: true });
});

cy.writeConsoleLogToFile

writeConsoleLogToFile: (
    outputDir: string,
    options?: WriteLogOptions & Partial<Cypress.WriteFileOptions & Cypress.Timeoutable>
) => Chainable<null>;

Write the logged console output to a file.

References:

Example

// when a test fails, log all console output to a file with formatted output
afterEach(function () {
    if (this.currentTest?.state === "failed") {
         cy.writeConsoleLogToFile("_console", { prettyOutput: true });
    }
});
// increase the timeout for `cy.writeFile` when you expect a big output
cy.writeConsoleLogToFile("_console", {
    timeout: 120000
});
// write only the console errors and unhandled JavaScript errors to the output file
cy.writeConsoleLogToFile("_console", {
    types: [ConsoleLogType.ConsoleError, ConsoleLogType.Error]
});
// filter all console output to include only entries starting with "Custom log:"
cy.writeConsoleLogToFile(outputDir, {
    filter: (type, ...args) => typeof args[0] === "string" && args[0].startsWith("Custom log:")
});

WatchTheConsole public methods

log

get log(): ConsoleLog[];

Return a copy of all logged console outputs.

setOptions

Same as cy.watchTheConsoleOptions.

writeLogToFile

Same as cy.writeConsoleLogToFile.

Interfaces

ConsoleLog

interface ConsoleLog {
    /**
     * The console output or the unhandled JavaScript error message and stack trace
     */
    args: unknown[];
    /**
     * The customized date and time in the format dd/MM/yyyy, hh:mm:ss.milliseconds. (for better visual checking)
     */
    currentTime: CurrentTime;
    /**
     * The getTime() of the Date when the console was logged (for future investigation)
     */
    dateTime: DateTime;
    /**
     * Console Type
     */
    type: ConsoleLogType;
}

ConsoleLogType

enum ConsoleLogType {
    ConsoleInfo = "console.info",
    ConsoleError = "console.error",
    ConsoleLog = "console.log",
    ConsoleWarn = "console.warn",
    // this is equal to a unhandled JavaScript error
    Error = "error"
}

WatchTheConsoleOptions

interface WatchTheConsoleOptions {
    /**
     * When the console output includes an object, it is highly recommended to set this option to `true`
     * because an object can change at runtime and may not match the object that was logged at that moment.
     * When set to `true`, it will deeply copy the object and remove any circular dependencies.
     */
    cloneConsoleArguments?: boolean;
}

WriteLogOptions

interface WriteLogOptions {
    /**
     * The name of the file. If `undefined`, it will be generated from the running test.
     */
    fileName?: string;
    /**
     * An option to filter the logged items
     *
     * @param type The type of the console log
     * @param args The console log arguments
     * @returns `false` if the item should be skipped
     */
    filter?: (type: ConsoleLogType, ...args: unknown[]) => boolean;
    /**
     * When set to `true`, the output JSON will be formatted with tabs
     */
    prettyOutput?: boolean;
    /**
     * "If the type is not provided, it logs all console entries
     */
    types?: ConsoleLogType[];
}

Websocket Interceptor

Getting started

It is very simple, just install the package using yarn or npm and import the package in your cypress/support/e2e.js, cypress/support/e2e.ts or in any of your test files:

import "cypress-interceptor/websocket";

The Cypress Websocket Interceptor commands

/**
 * Get an instance of the Websocket Interceptor
 *
 * @returns An instance of the Websocket Interceptor
 */
wsInterceptor: () => Chainable<WebsocketInterceptor>;
/**
 * Get the last call matching the provided route matcher.
 *
 * @param matcher A matcher
 * @returns The last call information or `undefined` if none matches.
 */
wsInterceptorLastRequest: (
    matcher?: IWSMatcher
) => Chainable<CallStackWebsocket | undefined>;
/**
 * Get the statistics for all requests matching the provided matcher since the beginning
 * of the current test.
 *
 * @param matcher A matcher
 * @returns It returns all requests matching the provided matcher with detailed information.
 * If none match, it returns an empty array.
 */
wsInterceptorStats: (matcher?: IWSMatcher) => Chainable<CallStackWebsocket[]>;
/**
 * Write the logged requests' information (or those filtered by the provided matcher) to a file
 *
 * @example cy.wsInterceptorStatsToLog("./out") => the output file will be "./out/Description - It.stats.json"
 * @example cy.wsInterceptorStatsToLog("./out", { fileName: "file_name" }) =>  the output file will be "./out/file_name.stats.json"
 * @example cy.wsInterceptorStatsToLog("./out", { matcher: { protocols: "soap" } }) => write only "soap" requests to the output file
 * @example cy.wsInterceptorStatsToLog("./out", { matcher: { url: "my-url" } }) => write only requests to my-url to the output file
 * @example cy.wsInterceptorStatsToLog("./out", { mapper: (entry) => ({ url: entry.url }) }) => map the output that will be written to the output file
 *
 * @param outputDir A path for the output directory
 * @param options Options
 */
wsInterceptorStatsToLog: (
    outputDir: string,
    options?: WriteStatsOptions & Partial<Cypress.WriteFileOptions & Cypress.Timeoutable>
) => Chainable<null>;
/**
 * Reset the the Websocket Interceptor's watch
 */
wsResetInterceptorWatch: VoidFunction;
/**
 * Wait until a websocket action occurs
 *
 * @param options Action options
 * @param errorMessage An error message when the maximum waiting time is reached
 */
waitUntilWebsocketAction(
    options?: WaitUntilActionOptions,
    errorMessage?: string
): Cypress.Chainable<WebsocketInterceptor>;
/**
 * Wait until a websocket action occurs
 *
 * @param matcher A matcher
 * @param errorMessage An error message when the maximum waiting time is reached
 */
waitUntilWebsocketAction(
    matcher?: IWSMatcher | IWSMatcher[],
    errorMessage?: string
): Cypress.Chainable<WebsocketInterceptor>;
/**
 * Wait until a websocket action occurs
 *
 * @param matcher A matcher
 * @param options Action options
 * @param errorMessage An error message when the maximum waiting time is reached
 */
waitUntilWebsocketAction(
    matcher?: IWSMatcher | IWSMatcher[],
    options?: WaitUntilActionOptions,
    errorMessage?: string
): Cypress.Chainable<WebsocketInterceptor>;
waitUntilWebsocketAction(
    matcherOrOptions?: IWSMatcher | IWSMatcher[] | WaitUntilActionOptions,
    errorMessageOrOptions?: string | WaitUntilActionOptions,
    errorMessage?: string
): Cypress.Chainable<WebsocketInterceptor>;

Cypress environment variables

Same as Cypress environment variables.

Documentation and examples

cy.wsInterceptor

wsInterceptor: () => Chainable<WebsocketInterceptor>;

Get an instance of the Websocket Interceptor

Example

cy.wsInterceptor().then(interceptor => {
    interceptor.resetWatch();

    intereptor.writeStatsToLog("_logs", { protocols: "soap" }, "stats");
});

cy.wsInterceptorLastRequest

Get the last call matching the provided route matcher.

wsInterceptorLastRequest: (matcher?: IWSMatcher) => Chainable<CallStackWebsocket | undefined>;

Example

cy.wsInterceptorLastRequest({ url: "some-url" }).should("not.be.undefined");
cy.wsInterceptorLastRequest({ type: "close" }).then((entry) => {
    expect(entry).not.to.be.undefined;
    expect(entry!.data).to.haveOwnProperty("code", code);
    expect(entry!.data).to.haveOwnProperty("reason", reason);
    expect(entry!.url.toString().endsWith("some-url")).to.be.true;
});

cy.wsInterceptorStats

Get the statistics for all requests matching the provided matcher since the beginning of the current test.

wsInterceptorStats: (matcher?: IWSMatcher) => Chainable<CallStackWebsocket[]>;

Example

cy.wsInterceptorStats({ type: "send" }).then((stats) => {
    expect(stats.length).to.eq(2);
    expect(stats[0].data).not.to.be.empty;
    expect(stats[1].data).not.to.be.empty;
});
cy.wsInterceptorStats({ type: "onmessage" }).then((stats) => {
    expect(stats.length).to.eq(2);
    expect(stats[0].data).to.haveOwnProperty("data", "some response 1");
    expect(stats[1].data).to.haveOwnProperty("data", "some response 2");
});

cy.wsInterceptorStatsToLog

wsInterceptorStatsToLog: (outputDir: string,options?: WriteStatsOptions  & Partial<Cypress.WriteFileOptions & Cypress.Timeoutable>) => Chainable<null>;

References:

Write the logged requests' information (or those filtered by the provided matcher) to a file. The file will contain the JSON.stringify of callStack.

Example

afterAll(() => {
    // the output file will be "./out/test.cy.ts (Description - It).stats.json" (the name of the file `test.cy.ts (Description - It)` will be generated from the running test)
    cy.wsInterceptorStatsToLog("./out");
    // increase the timeout for `cy.writeFile` when you expect a big output
    cy.wsInterceptorStatsToLog("./out", { timeout: 120000 });
    // the output file will be "./out/file_name.stats.json"
    cy.wsInterceptorStatsToLog("./out", { fileName: "file_name" });
    // write only stats for a specific URL to the output file
    cy.wsInterceptorStatsToLog("./out", { matcher: { url: "**/some-url" } });
        // write only "onmessage" actions to the output file
    cy.wsInterceptorStatsToLog("./out", { filter: (entry) => entry.type === "onmessage" });
    // map the output that will be written to the output file
    cy.wsInterceptorStatsToLog("./out", { mapper: (entry) => ({ type: entry.type, url: entry.url }) });
});

cy.wsResetInterceptorWatch

Reset the the Websocket Interceptor's watch

wsResetInterceptorWatch: () => void;

cy.waitUntilWebsocketAction

Wait until a websocket action occur

waitUntilWebsocketAction(
    options?: WaitUntilActionOptions,
    errorMessage?: string
): Cypress.Chainable<WebsocketInterceptor>;
waitUntilWebsocketAction(
    matcher?: IWSMatcher | IWSMatcher[],
    errorMessage?: string
): Cypress.Chainable<WebsocketInterceptor>;
waitUntilWebsocketAction(
    matcher?: IWSMatcher | IWSMatcher[],
    options?: WaitUntilActionOptions,
    errorMessage?: string
): Cypress.Chainable<WebsocketInterceptor>;

Example

// wait for the specific response
cy.waitUntilWebsocketAction({
    data: "some response",
    type: "onmessage"
});
// wait for the specific send
cy.waitUntilWebsocketAction({
    data: "some data",
    type: "send"
});
// wait for two sends
cy.waitUntilWebsocketAction(
    {
        type: "send"
    },
    { countMatch: 2 }
);
// wait for multiple actions
cy.waitUntilWebsocketAction([
    {
        data: "onmessage data",
        type: "onmessage",
        url: "**/path-1"
    },
    {
        data: "send data",
        type: "send",
        url: "**/path-2"
    },
    {
        data: "onmessage data",
        protocols: "xmpp",
        type: "onmessage",
        url: "**/path-3"
    }
]);
// wait for an action having a url filtered by RegExp
cy.waitUntilWebsocketAction({
    data: responseData12,
    type: "onmessage",
    url: new RegExp(`some-path$`, "i")
});
// wait for a specific close action with the code and reason equal to a specified value
cy.waitUntilWebsocketAction([
    {
        code: 1000,
        reason: "some reason",
        type: "close"
    }
]);

Websocket Interceptor public methods

callStack

Returns a copy of all logged requests since the WebSocket Interceptor was created (the Websocket Interceptor is created in beforeEach).

get callStack(): CallStackWebsocket[];

getLastRequest

Same as cy.wsInterceptorLastRequest.

getStats

Same as `cy.wsInterceptorStats.

resetWatch

Same as `cy.wsResetInterceptorWatch.

setOptions

Same as wsInterceptorOptions.

waitUntilWebsocketAction

Same as cy.waitUntilWebsocketAction.

writeStatsToLog

Same as cy.wsInterceptorStatsToLog.

Interfaces

IWSMatcher

type IWSMatcher = {
    /**
     * A matcher for the query string (URL search params)
     *
     * @param query The URL qearch params as an object
     * @returns `true` if matches
     */
    queryMatcher?: (query: Record<string, string | number>) => boolean;
    /**
     * Websocket protocols
     */
    protocols?: string | string[];
    /**
     * A URL matcher, use * or ** to match any word in string
     *
     * @example "**\/api/call" will match "http://any.com/api/call", "http://any.com/test/api/call", "http://any.com/test/api/call?page=99", ...
     * @example "*\api\*" will match "http://any.com/api/call", "http://any.com/api/list", "http://any.com/api/call-2?page=99&filter=1",
     * @example "**" will match any URL
     */
    url?: StringMatcher;
} & (
    | {
          types?: ("create" | "close" | "onclose" | "onerror" | "onopen" | "onmessage" | "send")[];
      }
    | {
          type?: "create" | "onerror" | "onopen";
      }
    | {
          code?: number;
          reason?: string;
          type?: "close";
      }
    | {
          code?: number;
          reason?: string;
          type: "onclose";
      }
    | {
          data?: string;
          type: "onmessage";
      }
    | {
          data?: string;
          type: "send";
      }
);

WriteStatsOptions

interface WriteStatsOptions {
    /**
     * The name of the file. If `undefined`, it will be generated from the running test.
     */
    fileName?: string;
    /**
     * An option to filter the logged items
     *
     * @param callStack Call information stored in the stack
     * @returns `false` if the item should be skipped
     */
    filter?: (callStack: CallStackWebsocket) => boolean;
    /**
     * An option to map the logged items
     *
     * @param callStack Call information stored in the stack
     * @returns Any object you want to log
     */
    mapper?: (callStack: CallStackWebsocket) => unknown;
    /**
     * A matcher
     */
    matcher?: IWSMatcher;
    /**
     * When set to `true`, the output JSON will be formatted with tabs
     */
    prettyOutput?: boolean;
}

test.unit

This is a simple helper designed to store arguments passed to lineCalled or lineCalledWithClone and then call them in your application for testing in Cypress. By default, the call line is not enabled — you must enable it first in your Cypress test. There's no need to worry; in the application, it does nothing and does not slow down performance.

How to use

In your application, you can call lineCalled or lineCalledWithClone. anywhere you need. Then, enable it in your Cypress test setup before running the test:

import { enableCallLine } from "cypress-interceptor/test.unit";

beforeEach(() => {
    cy.window().then((win) => {
        enableCallLine(win);
    });
});

Globally in your cypress/support/e2e.js or cypress/support/e2e.ts:

Cypress.on("window:before:load", (win) => {
    enableCallLine(win);
});

And in your tests you will be able to use it as follows:

import "cypress-interceptor/test.unit.commands";

// check that call line is really enabled
cy.callLine().its('isEnabled').should('be.true');

cy.callLineLength().should("eq", 0);
// OR cy.callLine().invoke("length").should("eq", 0);

// do some action which should call `lineCalled` or `lineCalledWithClone`
cy.contains("button", "Add").click();

cy.callLineNext().should("eq", "Some value");
// OR cy.callLineNext().should("deep.eq", ["some Value", { } ]);
// OR cy.callLine().then(callLine => expect(callLine.next).to.eq("something"));

The test.unit Cypress commands

/**
 * Get a created instance of the CallLine class
 *
 * @returns An instance of the CallLine class
 */
callLine(): Chainable<CallLine>;
/**
 * Clean the CallLine array and start storing the values from the beginning
 */
callLineClean(): void;
/**
 * The last existing entry. It can be `undefined` if there is no entry at
 * the moment or `next` has not been called. Otherwise it always returns
 * the last entry invoked by `next`.
 */
callLineCurrent(): Chainable<unknown | unknown[] | undefined>;
/**
 * Get the number of all entries.
 */
callLineLength(): Chainable<number>;
/**
 * Get the next entry. If there is no next entry, it returns undefined.
 *
 * If the entry was added as a single argument like `lineCalled("something")`,
 * it will return the single value "something". But if it was added as multiple
 * arguments like `lineCalled("something", 1, true)`, it will return an array
 * `["something", 1, true]`.
 */
callLineNext(): Chainable<unknown | unknown[] | undefined>;
/**
 * Resets the counter and starts from the first entry on the next call to `cy.callLineNext`
 */
callLineReset(): void;

cy.callLine

callLine(): Chainable<CallLine>;

Get a created instance of the CallLine class

Example

// check that call line is really enabled
cy.callLine().its('isEnabled').should('be.true');

cy.callLineClean

callLineClean(): void;

Clean the CallLine array and start storing the values from the beginning

cy.callLineCurrent

callLineCurrent(): Chainable<unknown | unknown[] | undefined>;

The last existing entry. It can be undefined if there is no entry at the moment or next has not been called. Otherwise it always returns the last entry invoked by next.

Example

// wait for the next entry
cy.callLineNext().should("not.be.undefined");
// do some checking
cy.callLineCurrent().should("eq", "my custom string");
// do more checkings with the last entry
cy.callLineCurrent().should("eq", ...);

cy.callLineLength

callLineLength(): Chainable<number>;

Get the number of all entries.

Example

// uses a Cypress query to check the total number of entries
cy.callLineLength().should("eq", 1);

cy.callLineNext

callLineNext(): Chainable<unknown | unknown[] | undefined>;

Get the next entry. If there is no next entry, it returns undefined. It is a Cypress query.

If the entry was added as a single argument like lineCalled("something"), it will return the single value "something". But if it was added as multiple arguments like lineCalled("something", 1, true), it will return an array ["something", 1, true].

Example

// uses a Cypress query to check if the next entry (different from undefined) is `123`
// in combination with `should` it tries to call `callLine.next` until it is not undefined
cy.callLineNext().should("eq", 123);

cy.callLineReset

callLineReset(): void;

Resets the counter and starts from the first entry on the next call to cy.callLineNext

Documentation and examples of cypress-interceptor/test.unit

enableCallLine

/**
 * @param childWindow A window instance. In Cypress, it must be the window of `cy.window()`
 */
enableCallLine(childWindow?: CallLineWindowType): void;

Enable the call line in the window object.

Example

// to enable it in the current run only outside the web browser
enableCallLine();
// to enable it in the current run and the web browser
cy.window().then((win) => {
    enableCallLine(win);
});
Cypress.on("window:before:load", (win) => {
    enableCallLine(win);
});

isCallLineEnabled

/**
 * @returns True if the call line is enabled
 */
isCallLineEnabled(): boolean;

Check if the call line is enabled.

getCallLine

/**
 * @returns An instance of the CallLine class
 */
getCallLine(): CallLine;

References:

Get a created instance of the CallLine class.

lineCalled

/**
 * @param args Anything that you want to store
 */
lineCalled(...args: unknown[]): void;

This is the main function that should be in your application to store any information you need.

For storing information about the line that was called. If there are more arguments, it will be saved as an array, otherwise it will be stored as a single value.

lineCalledWithClone

lineCalledWithClone(...args: unknown[]): void;

Similar to lineCalled, but with cloned arguments. This prevents any changes to object references in the arguments over time.

Interfaces

CallLine

interface CallLine {
    /**
     * Get an instance of the window or an empty array if is not enabled
     */
    array: unknown[];

    /**
     * The last existing entry. It can be `undefined` if there is no entry at
     * the moment or if `next` has not been called. Otherwise, it always returns
     * the last entry invoked by `next`.
     */
    current: unknown | unknown[] | undefined;

    /**
     * True if CallLine feature is globally enabled
     */
    isEnabled: boolean;

    /**
     * Get the number of all entries.
     */
    length: number;

    /**
     * Get the next entry. If there is no next entry, it returns `undefined`.
     *
     * If the entry was added as a single argument like `lineCalled("something")`,
     * it will return the single value "something". But if it was added as multiple
     * arguments like `lineCalled("something", 1, true)`, it will return an array
     * `["something", 1, true]`.
     */
    next: unknown | unknown[] | undefined;

    /**
     * Clean the CallLine array and start storing the values from the beginning
     */
    clean: () => void;

    /**
     * Resets the counter and starts from the first entry on the next call to `next`
     */
    reset: () => void;
}
3.8.1

4 months ago

3.8.0

5 months ago

3.7.1

6 months ago

3.7.0

6 months ago

3.6.0

6 months ago

3.4.0

6 months ago

3.5.0

6 months ago

3.4.1

6 months ago

3.3.0

6 months ago

3.2.0

6 months ago

3.1.0

6 months ago

3.0.0

6 months ago

2.3.0

1 year ago

2.2.0

1 year ago

2.4.0

1 year ago

2.1.0

1 year ago

2.0.0

1 year ago

1.5.0

1 year ago

1.2.0

1 year ago

1.4.0

1 year ago

1.3.0

1 year ago

1.1.2

1 year ago

1.1.1

1 year ago

1.1.0

1 year ago

1.0.0

1 year ago