/* eslint-disable @typescript-eslint/no-namespace */ /// import { setDocumentResolver, addInteractionWrapper } from '@interactors/globals'; import { Interaction, isInteraction, AssertionInteraction } from '@interactors/core'; declare global { namespace Cypress { interface Chainable { do(interaction: Interaction | Interaction[]): Chainable; expect(interaction: AssertionInteraction | AssertionInteraction[]): Chainable; expectThat(interaction: AssertionInteraction | AssertionInteraction[]): Chainable; } } } let cypressCommand: CypressCommand | null = null type CypressCommand = 'expect' | 'do' setDocumentResolver(() => cy.$$('body')[0].ownerDocument); addInteractionWrapper((operation, interaction) => () => { if (interaction.type == 'action' && cypressCommand == 'expect') throw new Error(`tried to ${interaction.description} in \`cy.expect\`, actions/perform should only be run in \`cy.do\``); return operation } ); function interact(interactions: Interaction[], command: CypressCommand): void { interactions // eslint-disable-next-line @typescript-eslint/no-explicit-any .reduce((cy: Cypress.Chainable, interaction) => cy .then(() => interaction) .then(() => { Cypress.log({ displayName: command, message: interaction.description }); }), cy.then(() => void (cypressCommand = command)) ).then(() => (cypressCommand = null)) }; function isInteractions(interactions: unknown[]): interactions is AssertionInteraction[] { return interactions.every(isInteraction) } if (typeof Cypress !== 'undefined' ) { Cypress.Commands.add('do', (interaction: Interaction | Interaction[]) => interact(([] as Interaction[]).concat(interaction), 'do') ); // NOTE: Save the original `expect` assertion method let chaiExpect = cy.expect as (value: unknown, message?: string) => unknown let interactionExpect = (interaction: AssertionInteraction | AssertionInteraction[]) => ( interact(([] as AssertionInteraction[]).concat(interaction), 'expect') ) try { // NOTE: Add interaction assertion function, Cypress also overrides `expect` method to a wrapper function // This need for Cypress <10 versions Cypress.Commands.add('expect', interactionExpect); } catch (e) {} if ('_commands' in Cypress.Commands) { // @ts-expect-error Cypress stores a reference to commands object and use it to check overwritability of commands // https://github.com/cypress-io/cypress/blob/d378ec423a4a2799f90a6536f82e4504bc8b3c9e/packages/driver/src/cypress/commands.ts#L155 Cypress.Commands._commands['expect'] = interactionExpect; // NOTE: Add interaction assertion function, Cypress also overrides `expect` method to a wrapper function // @ts-expect-error TypeScript complains that signature doesn't match with declared one Cypress.Commands.overwrite('expect', interactionExpect); } // NOTE: Fallback alias for `expect` assertion method Cypress.Commands.add('expectThat', interactionExpect); // NOTE: Override Cypress's wrapper to our combined `expect` // @ts-expect-error TypeScript complains that signature doesn't match with declared one cy.expect = ( interaction: AssertionInteraction | AssertionInteraction[] | unknown, message?: string ) => { let interactions = Array.isArray(interaction) ? interaction : [interaction] if (isInteractions(interactions)) { return interactionExpect(interactions) } else { return chaiExpect(interaction, message) } } };