简体   繁体   中英

JS: Pass an Object containing a Function/Method Property then Passing and Invoking the Passed in Object Function/Method Property in Another File?

I'm working on adding an additional check for a Login Function. If the object passed into the function contains a property name additionalSteps, it will then invoke additionalSteps property. However, whenever I get to the invoking step, I get an error stating the property is not a function or is undefined. (Depends on how I have adjusted the code by invoking the object property( options.additionalSteps ) or assigning a local variable to object property( let additionalSteps ).)

I want to know if it's possible to pass an Object which has the property that defines a function/method, then pass that Object into a function from an imported class(or uses require ) which will check if the property name exists then invoke that Object's function if the condition is true. I could have this other file define and handle the function, but I wanted the user to define the property function however they desire.

I get the feeling with properties this is not possible to pass a function/method through another file within an Object based on my research on the subject. (All the information and examples on object property with functions, seem to imply that this wasn't supported.)

Test Code:

async function fewMoreSteps({page, options} = {}) {
  console.log('This is the addtional step...')
  await page.waitForSelector('#header_notification_link', {visible: true})
  await page.click('#header_notification_link')
}


describe('Test Login', () => {
  // eslint-disable-next-line jest/no-focused-tests
  it.only('Login with custom function', () => {
    const username = Cypress.env('steamUserName')
    const password = Cypress.env('steamUserNamePW')
    const loginUrl = Cypress.env('loginUrl')
    const loginSelector = Cypress.env('loginSelector')
    const cookieName = Cypress.env('cookieName')
    const socialLoginOptions = {
      username,
      password,
      loginUrl,
      // Add username/pw fields and buttons and addtional steps
      usernameField: '#input_username',
      passwordField: '#input_password',
      passwordSubmitBtn: '#login_btn_signin',
      // make this a global passed function
      additionalSteps: async function({page, options} = {}) {
        /*console.log('This is the addtional step...')
        await page.waitForSelector('#header_notification_link', {visible: true})
        await page.click('#header_notification_link') */ 
        await fewMoreSteps({page, options})
      },
      isPopup: true,
      popupDelay: 6000,
      logs: true,
      headless: false,
      loginSelector: loginSelector,
      postLoginClick: '#account_pulldown',
      postLoginSelector: '#account_dropdown div.popup_menu a.popup_menu_item:first-of-type'
    }

    cy.log(socialLoginOptions)

    return cy.task('customizedLogin', socialLoginOptions, {timeout: 300000}).then(({cookies, lsd, ssd, additionsFound}) => {
      cy.log(additionsFound)
      cy.clearCookies()

Cypress task plugins:

const {customizedLogin} = require('../../src/Plugins').CustomizedLogin

module.exports = (on, config) => {
  // `on` is used to hook into various events Cypress emits
  // `config` is the resolved Cypress config
  on('task', {
    customizedLogin: CustomizedLogin
  })
}

Inside the CustomizedLogin and later invoked when AdditionalSteps is Invoked:

module.exports.CustomizedLogin = async function CustomizedLogin(options = {}) {
  if (options.usernameField && options.passwordField) {
    const typeUsername = async function({page, options} = {}) {
      await page.waitForSelector(options.usernameField, {visible: true})
      await page.type(options.usernameField, options.username)
      if (options.usernameSubmitBtn) {
        await page.click(options.usernameSubmitBtn)
      }
    }
    const typePassword = async function({page, options} = {}) {
      await page.waitForSelector(options.passwordField, {visible: true})
      await page.type(options.passwordField, options.password)
      if (options.passwordSubmitBtn) {
        await page.click(options.passwordSubmitBtn)
      }
    }

    let additionalSteps = null
    if (options.additionalSteps != 'undefined') {
      // main concern with this step
      additionalSteps = options.additionalSteps
    }

    const postLogin = async function ({page, options} = {}) {
      await page.waitForSelector(options.postLoginClick)
      await page.click(options.postLoginClick)
    }

    return baseLoginConnect(typeUsername, typePassword, null, null, options.additionalSteps, postLogin, options)
  } else {
    throw new Error('Please review your option properties. Propeties usernameField and passwordField are required as type String.')
  }

// several lines down the within baseLoginConnect when the function is invoked 

 let additionsFound = false
  console.log('Before checking for addtional steps...')
  if (options.additionalSteps != 'undefined') {
    console.log('perform additional steps')
    console.log(additionalSteps)
    await options.additionalSteps({page, options})
    additionsFound = true
  }

Fixed my issue. The problem was coming from test file. It doesn't seem objects with function properties go through well with cy.task() . (Probably had trouble trying to reference the addtionalSteps function. Would need to research whether my initial attempt was possible within Cypress.) Instead I had the additionStep check added in Cypress index file for plugins.

Cypress Plugins:

const {customizedLogin} = require('../../src/Plugins').CustomizedLogin

async function fewMoreSteps({page, options} = {}) {
  console.log('This is the addtional step...')
  await page.waitForSelector('#header_notification_link', {visible: true, timeout: 6000})
  await page.click('#header_notification_link')
}

module.exports = (on, config) => {
  // `on` is used to hook into various events Cypress emits
  // `config` is the resolved Cypress config
  on('task', {
    customizedLogin: (options) => {
      if (options.moreSteps) {
        options.additionalSteps = fewMoreSteps
      }
      return CustomizedLogin(options)
    }
  }
  )
}

Now I don't need to add extra variables for CustomizedLogin function and when addtionalSteps is invoked, (expect for a check condition.)

module.exports.CustomizedLogin = async function CustomizedLogin(options = {}) {
  if (options.usernameField && options.passwordField) {
    const typeUsername = async function({page, options} = {}) {
      await page.waitForSelector(options.usernameField, {visible: true})
      await page.type(options.usernameField, options.username)
      if (options.usernameSubmitBtn) {
        await page.click(options.usernameSubmitBtn)
      }
    }
    const typePassword = async function({page, options} = {}) {
      await page.waitForSelector(options.passwordField, {visible: true})
      await page.type(options.passwordField, options.password)
      if (options.passwordSubmitBtn) {
        await page.click(options.passwordSubmitBtn)
      }
    }
    const postLogin = async function ({page, options} = {}) {
      await page.waitForSelector(options.postLoginClick)
      await page.click(options.postLoginClick)
    }
    return baseLoginConnect(typeUsername, typePassword, null, null, postLogin, options)
  } else {
    throw new Error('Please review your option properties. Propeties usernameField and passwordField are required as type String.')
  }
}

// When options.addtionalSteps is invoked
if (typeof options.additionalSteps !== 'undefined') {
    await options.additionalSteps({page, options})
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM