import { Page, Frame, FrameLocator, Locator } from '@playwright/test';

type NestedCollection<T> = T & {
    first(): T;
    last(): T;
    nth(index: number): T;
};

type Handle = Page | Frame | FrameLocator | Locator;

interface SelectorOptions {
    /**
     * When defined, creates a frame locator which the element will be nested
     * inside of.
     */
    frame?: string;
    /**
     * When true, the locator will be based off the `frame`, rather than the
     * `root` thus escaping from any collection nesting. This is useful to
     * represent a page structure whose visual appearance differs from it's
     * DOM structure.
     *
     * @default false
     */
    portal?: boolean;
}
type Method = 'locator' | 'getByAltText' | 'getByLabel' | 'getByPlaceholder' | 'getByRole' | 'getByTestId' | 'getByText' | 'getByTitle';
interface EmptyObject {
}
type Arg<T extends Method> = Parameters<Page[T]>[0];
type Options<T extends Method> = (Parameters<Page[T]>[1] extends undefined ? EmptyObject : Parameters<Page[T]>[1]) & SelectorOptions;
interface EnhancedPageMethod<T extends Method> {
    (arg: Arg<T>, options?: Options<T>): Locator;
}
declare class Collection<T extends Handle = Locator> {
    root: T;
    protected getByAltText: EnhancedPageMethod<'getByAltText'>;
    protected getByLabel: EnhancedPageMethod<'getByLabel'>;
    protected getByPlaceholder: EnhancedPageMethod<'getByPlaceholder'>;
    protected getByRole: EnhancedPageMethod<'getByRole'>;
    protected getByTestId: EnhancedPageMethod<'getByTestId'>;
    protected getByText: EnhancedPageMethod<'getByText'>;
    protected getByTitle: EnhancedPageMethod<'getByTitle'>;
    constructor(root: T);
    /**
     * Retrieve a locator to a given element on the page identified by the
     * selector.
     *
     * @param selector - The selector that identifies the element.
     * @param options - Options for how to build the locator.
     */
    protected el(selector: string, { frame, portal, ...options }?: Options<'locator'>): Locator;
    /**
     * Nest another collection inside of the current collection. Locators in the
     * nested collection will be based on the `root` argument which can be
     * customized to be a selector, locator, page, etc.
     *
     * @param collection - Uninstantiated collection class to nest.
     * @param root - The root of the nested collection. This could be a static
     * string, an element from the parent collection, or the collection's frame.
     *
     * @example this.nest(TextField, '#my-text-field')
     * @example this.nest(TextField, this.form)
     * @example this.nest(TextField, this.frame)
     */
    protected nest<U>(collection: new (root: Locator) => U, root: string | Locator): NestedCollection<U>;
    protected nest<U>(collection: new (root: FrameLocator) => U, root: string | FrameLocator): NestedCollection<U>;
    protected nest<U, Root extends Page | Frame>(collection: new (root: Root) => U, root: Root): U;
    /**
     * Returns the frame that the collection is attached to. This can be used when
     * nesting a collection if the nested collection's locators elements should
     * be based off the page or frame rather than the parent collection's root.
     *
     * If the root of the collection is a page, then the main frame of the page
     * will be returned.
     *
     * @example this.nest(TextField, this.frame)
     */
    get frame(): Frame;
    /**
     * Returns the page that the collection is attached to. This can be used when
     * you need to access page methods inside your collection utility methods. For
     * example, if a utility needs to access `page.mouse`, this will allow that.
     *
     * @example this.page.mouse.down()
     */
    get page(): Page;
    private getParent;
    private enhanceMethod;
}

export { Handle, SelectorOptions, Collection as default };
