【问题标题】:JS: Pass an Object containing a Function/Method Property then Passing and Invoking the Passed in Object Function/Method Property in Another File?JS:传递一个包含函数/方法属性的对象,然后在另一个文件中传递和调用传入的对象函数/方法属性?
【发布时间】:2021-03-11 11:51:37
【问题描述】:

我正在为登录功能添加额外的检查。如果传递给函数的对象包含一个属性名additionalSteps,那么它将调用additionalSteps 属性。但是,每当我进入调用步骤时,都会收到一条错误消息,指出该属性不是函数或未定义。 (取决于我如何通过调用对象属性(options.additionalSteps)或将局部变量分配给对象属性(let additionalSteps)来调整代码。)

我想知道是否可以传递一个具有定义函数/方法的属性的对象,然后将该对象从导入的类(或使用require)传递给一个函数,这将检查属性名称是否如果条件为真,则存在然后调用该对象的函数。我可以让这个其他文件定义和处理函数, 但我希望用户根据自己的意愿定义属性函数。

根据我对该主题的研究,我对属性的感觉是不可能通过对象中的另一个文件传递函数/方法。 (关于带有函数的对象属性的所有信息和示例,似乎都暗示这不受支持。)

测试代码:

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 任务插件:

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
  })
}

在 CustomizedLogin 内部,稍后在调用 AdditionalSteps 时调用:

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
  }

【问题讨论】:

  • “传递到另一个文件”是什么意思?
  • options.additionalSteps != 'undefined'。我不认为这和你想的一样。
  • 我的意思是导入或需要一个类(一个文件可能包含多个类,这就是我使用该术语的原因。我更新了我的帖子)
  • @Titus,我并不关心这个条件检查,因为如果条件为真,我更关心调用options.additionalSteps({page, options})。我最初使用typeof 作为检查条件,但无法确定该函数是否被执行。

标签: javascript node.js function object cypress


【解决方案1】:

解决了我的问题。问题来自测试文件。 cy.task() 似乎不能很好地处理具有函数属性的对象。 (可能在尝试引用 addtionalSteps 函数时遇到问题。需要研究我的初始尝试是否可以在赛普拉斯中进行。)相反,我在赛普拉斯索引文件中添加了 addStep 检查插件。

赛普拉斯插件:

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)
    }
  }
  )
}

现在我不需要为 CustomizedLogin 函数添加额外的变量,并且在调用 addtionalSteps 时,(需要检查条件。)

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})
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-26
    • 2013-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多