Skip to content

Instantly share code, notes, and snippets.

@NicholasBoll
Last active May 14, 2022 22:25
Show Gist options
  • Save NicholasBoll/4fe67bf4ec4784d028ef6bea0e699da8 to your computer and use it in GitHub Desktop.
Save NicholasBoll/4fe67bf4ec4784d028ef6bea0e699da8 to your computer and use it in GitHub Desktop.
interface AliasFn<Subject> {
(): Cypress.Chainable<Subject>
alias: string
}
declare namespace Cypress {
interface Chainable<Subject> {
createAlias(): AliasFn<Subject>
getAliases<T1, T2, T3, T4>(values: [AliasFn<T1>, AliasFn<T2>, AliasFn<T3>, AliasFn<T4>]): Chainable<[T1, T2, T3, T4]>
getAliases<T1, T2, T3>(values: [AliasFn<T1>, AliasFn<T2>, AliasFn<T3>]): Chainable<[T1, T2, T3]>
getAliases<T1, T2>(values: [AliasFn<T1>, AliasFn<T2>]): Chainable<[T1, T2]>
getAliases<T>(values: (AliasFn<T>)[]): Chainable<T[]>
}
}
// We use 'before' because Cypress throws an error if commands are used outside the context of a test
before(() => {
// We need to access the $Chainer prototype directly. It is not exposed except
// when a $Chainer is created by a Cypress command and then it is on __proto__
// This seems very hacky, but the __proto__ is widely supported
;(cy.wrap('') as any).__proto__.createAlias = function<Subject>(): AliasFn<Subject> {
// Create a unique identifier. We don't need to access it directly
const id = Cypress._.uniqueId('alias_')
// Save the current subject under a private alias. We have no other way of getting
// the subject out of the chain
this.as(id)
// This function will return a cy.get that can access our private alias
const aliasFn = (() => cy.get<Subject>(`@${id}`, { log: false })) as AliasFn<Subject>
aliasFn.alias = id
return aliasFn
}
})
Cypress.Commands.add('getAliases', { prevSubject: false }, function(
this: Mocha.ITestCallbackContext,
aliases: AliasFn<any>[]
) {
return cy.wrap('', { log: false }).then(() => {
return aliases.map(alias => this[alias.alias])
})
})
@DragorWW
Copy link

DragorWW commented May 14, 2022

hi @NicholasBoll, i update your code by new TypeScript feature. Can you update your gist ?

Or may be create npm package. I understand that you are using a private cypress API,
but i know a few people who use this code in production. Maybe a centralized package would be better than copy-past this solution.

interface AliasFn<Subject> {
    (): Cypress.Chainable<Subject>
    alias: string
}

declare namespace Cypress {
    interface Chainable<Subject> {
        createAlias(): AliasFn<Subject>
        getAliases<T extends AliasFn<any>[]>(...data: T): Chainable<{ [P in keyof T]: T[P] extends AliasFn<infer S> ? S : never}>
    }
}


// We use 'before' because Cypress throws an error if commands are used outside the context of a test
before(() => {
    // We need to access the $Chainer prototype directly. It is not exposed except
    // when a $Chainer is created by a Cypress command and then it is on __proto__
    // This seems very hacky, but the __proto__ is widely supported
    ; (cy.wrap('') as any).__proto__.createAlias = function <Subject>(): AliasFn<Subject> {
        // Create a unique identifier. We don't need to access it directly
        const id = Cypress._.uniqueId('alias_')

        // Save the current subject under a private alias. We have no other way of getting
        // the subject out of the chain
        this.as(id)

        // This function will return a cy.get that can access our private alias
        const aliasFn = (() => cy.get<Subject>(`@${id}`, { log: false })) as AliasFn<Subject>

        aliasFn.alias = id
        return aliasFn
    }
})

Cypress.Commands.add('getAliases', { prevSubject: false }, function (
    this: Cypress.ObjectLike,
    ...aliases: AliasFn<any>[]
) {
    return cy.wrap('', { log: false }).then(() => {
        return aliases.map(alias => this[alias.alias])
    })
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment