You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cypress origin命令内调用类方法报错:this.signIn is not a function

问题分析

报错this.signIn is not a function的核心原因是:cy.origin的回调函数运行在跨域页面的独立执行上下文中,和主测试上下文完全隔离,外部类实例的this无法穿透到这个环境,且外部页面对象的方法也无法直接在该环境中调用。即使在回调内用Cypress.require重新引入页面对象,也会创建全新的类实例,和外部的属性(如oktaServer)无关联,还可能因路径问题加载失败。

解决方案

方案1:将signIn改为静态方法

把signIn和相关依赖方法改为类的静态方法,消除对类实例的依赖,同时在cy.origin回调内重新引入类来调用方法:

修改页面对象代码:

export class OktaLoginPage {
  static oktaServer = Cypress.env('oktaServer'); 

  static login(username, password) {
    const loginArgs = {username, password};
    if (this.domainsMatch(this.oktaServer)) {
      this.signIn(username, password);
    } else {
      cy.origin(this.oktaServer, {args: loginArgs}, ({username, password}) => {
        // 在origin上下文内重新引入类
        const { OktaLoginPage } = require('../../support/page-objects/oktaLogin.po');
        OktaLoginPage.signIn(username, password);
      })
    }
  }

  static signIn(username, password) {
    cy.contains('h2', 'Sign In');
    cy.get('input[name="username"]').clear().type(username);
    cy.get('input[type="submit"]').invoke('attr', 'value').then(btnValue => {
      if (btnValue === 'Next') {
        cy.get('input[type="submit"]').click();
      }
    });
    cy.get('input[type="password"]').clear().type(password);
    cy.get('input[type="submit"]').click();
  }

  static domainsMatch(server) {
    // 保留你的domainsMatch逻辑,改为静态方法
    return cy.url().then(url => url.includes(server));
  }
}

测试中调用方式:

import { OktaLoginPage } from '../../support/page-objects/oktaLogin.po';

OktaLoginPage.login('test-username', 'test-password');

方案2:提取signIn为独立函数

将登录逻辑抽成单独的可复用函数,在主上下文和cy.origin回调内分别引入:

  1. 创建独立的signIn.js模块:
export function signIn(username, password) {
  cy.contains('h2', 'Sign In');
  cy.get('input[name="username"]').clear().type(username);
  cy.get('input[type="submit"]').invoke('attr', 'value').then(btnValue => {
    if (btnValue === 'Next') {
      cy.get('input[type="submit"]').click();
    }
  });
  cy.get('input[type="password"]').clear().type(password);
  cy.get('input[type="submit"]').click();
}
  1. 修改页面对象:
import { signIn } from './signIn';

export class OktaLoginPage {
  oktaServer = Cypress.env('oktaServer'); 

  login(username, password) {
    const loginArgs = {username, password};
    if (this.domainsMatch(this.oktaServer)) {
      signIn(username, password);
    } else {
      cy.origin(this.oktaServer, {args: loginArgs}, ({username, password}) => {
        // 注意路径要适配origin上下文的执行位置
        const { signIn } = require('./signIn');
        signIn(username, password);
      })
    }
  }

  domainsMatch(server) {
    // 保留你的domainsMatch逻辑
    return cy.url().then(url => url.includes(server));
  }
}

方案3:封装为Cypress自定义命令

把登录逻辑直接封装成自定义命令,简化跨域上下文的处理:

在cypress/support/commands.js中添加:

Cypress.Commands.add('oktaLogin', (username, password) => {
  const oktaServer = Cypress.env('oktaServer');
  
  const signIn = (user, pass) => {
    cy.contains('h2', 'Sign In');
    cy.get('input[name="username"]').clear().type(user);
    cy.get('input[type="submit"]').invoke('attr', 'value').then(btnValue => {
      if (btnValue === 'Next') {
        cy.get('input[type="submit"]').click();
      }
    });
    cy.get('input[type="password"]').clear().type(pass);
    cy.get('input[type="submit"]').click();
  };

  cy.url().then(currentUrl => {
    if (currentUrl.includes(oktaServer)) {
      signIn(username, password);
    } else {
      cy.origin(oktaServer, {args: {username, password}}, ({username, password}) => {
        signIn(username, password);
      });
    }
  });
});

测试中直接调用:

cy.oktaLogin('test-username', 'test-password');
关键注意事项
  • cy.origin的回调是沙箱环境,外部变量、类实例无法直接访问,必须通过args传递数据,或在回调内部重新引入依赖。
  • 若在回调内使用require,路径需相对于Cypress项目根目录,或使用绝对路径确保模块加载正常。
  • 避免在cy.origin内依赖外部类实例的状态,两个上下文完全隔离,状态无法共享。

内容的提问来源于stack exchange,提问作者Jim Kennedy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 09:00:21