Skip to main content
Cypress应用

社交登录认证

info
学习内容
  • 如何使用cy.origin()通过Auth0租户进行认证
  • 如何使用cy.session()缓存已认证用户
caution

不推荐在CI中使用

Cypress推荐将测试社交连接认证作为主要的认证测试手段。这是由于我们在最佳实践指南中提到的挑战

在CI中依赖社交认证很可能触发机器人检测机制,并可能因违反服务提供商条款而导致账号暂停。

应用设置

本指南将配置一个使用Auth0租户的应用,该租户已设置Google、Facebook和Microsoft的社交连接,可代表用户向我们的应用进行认证。

设置Auth0租户

首先创建一个免费的Auth0账号,选择我是新Auth0用户流程。包含4个步骤:

  • 步骤1: 配置示例应用时,请为您的平台选择单页应用,技术栈选择React
  • 步骤2:社交连接中,请选择FacebookMicrosoftGoogle
  • 步骤3: 尝试登录并验证连接是否设置正确
  • 步骤4: 可通过下载示例应用按钮或此处克隆获取示例。下一步需要此应用。选择我已准备好使用Auth0。现在您的Auth0租户社交连接应已正确配置

设置应用

在Auth0仪表板左侧菜单访问应用下拉框,选择下拉框并进入应用

  • 选择默认应用
  • 访问设置标签页
    • 复制基本信息中的domainclient ID字段,后续会用到
    • 应用属性应用类型应选择单页应用
    • 应用URI中,请将http://localhost:3000添加到允许的回调URL允许的Web来源允许的登出URL
  • 按照示例应用说明,从src/auth_config.json.example创建src/auth_config.json文件
  • 创建后,将domainclient ID粘贴到src/auth_config.json对应字段。可移除audience键。登录/回调URI默认为http://localhost:3000,即测试应用的URL
  • 安装应用依赖并启动开发服务器
  • 访问http://localhost:3000验证应用运行正常,点击右上角登录按钮。选择任一社交连接登录并确保完成授权应用提示(只需操作一次)。这将带您回到已认证的示例应用。完成后点击登出下拉按钮。对Google、Microsoft和Facebook或您想测试的账号重复此过程

以下是Facebook设置的过程示例:

使用Cypress测试

现在应用已设置完毕,可以编写自动化测试了。

caution

对于这些社交连接,必须启用experimentalModifyObstructiveThirdPartyCode配置选项。

在Cypress中设置Auth0应用凭证

要在测试中使用社交账号凭证,需要配置Cypress通过cypress.env.json文件或支持的方法设置社交用户名、密码和名称环境变量。

cypress.env.json
{
"GOOGLE_USERNAME": "",
"GOOGLE_PASSWORD": "",
"GOOGLE_NAME": "",
"MICROSOFT_USERNAME": "",
"MICROSOFT_PASSWORD": "",
"MICROSOFT_NAME": "",
"FACEBOOK_USERNAME": "",
"FACEBOOK_PASSWORD": "",
"FACEBOOK_NAME": ""
}

使用cy.origin()登录

我们将编写名为loginToAuth0ViaSocial的自定义命令,用于通过Facebook、Google或Microsoft登录。该命令将使用cy.origin()来:

  1. 导航到Auth0登录页
  2. 选择**继续使用...**按钮并使用社交凭证登录
  3. 登录后重定向回示例应用
  4. 使用cy.session()缓存结果

为演示,我们为每个提供商编写一个函数。

Facebook登录函数

cypress/support/commands.ts
import { domain as Auth0Domain } from '../../src/auth_config.json'

function logIntoFacebook(username: string, password: string, name: string) {
cy.visit('http://localhost:3000')
cy.get('#qsLoginBtn').click()

cy.origin(Auth0Domain, () => {
cy.scrollTo('bottom')
cy.get('form[data-provider="facebook"]').submit()
})

cy.origin(
'https://www.facebook.com',
{
args: {
username,
password,
},
},
({ username, password }) => {
cy.get('input#email').type(username)
cy.get('input#pass').type(password, {
log: false,
})
cy.get('[type="submit"]').contains('Log In').click()
}
)

cy.get('h6.dropdown-header').should('contain', name)
}

Google登录函数

cypress/support/commands.ts
import { domain as Auth0Domain } from '../../src/auth_config.json'

function logIntoGoogle(username: string, password: string, name: string) {
Cypress.on(
'uncaught:exception',
(err) =>
!err.message.includes('ResizeObserver loop') &&
!err.message.includes('Error in protected function')
)
cy.visit('http://localhost:3000')
cy.get('#qsLoginBtn').click()

cy.origin(Auth0Domain, () => {
cy.scrollTo('bottom')
cy.get('form[data-provider="google"]').submit()
})

cy.origin(
'https://accounts.google.com',
{
args: {
username,
password,
},
},
({ username, password }) => {
Cypress.on(
'uncaught:exception',
(err) =>
!err.message.includes('ResizeObserver loop') &&
!err.message.includes('Error in protected function')
)

cy.get('input[type="email"]').type(username, {
log: false,
})
// 注意:元素存在于原始表单但被隐藏并重新渲染,可能导致间歇性DOM分离问题
cy.contains('Next').click().wait(4000)
cy.get('[type="password"]').type(password, {
log: false,
})
cy.contains('Next').click().wait(4000)
}
)

cy.get('h6.dropdown-header').should('contain', name)
}

Microsoft登录函数

cypress/support/commands.ts
import { domain as Auth0Domain } from '../../src/auth_config.json'

function logIntoMicrosoft(username: string, password: string, name: string) {
cy.visit('http://localhost:3000')
cy.get('#qsLoginBtn').click()

cy.origin(Auth0Domain, () => {
cy.scrollTo('bottom')
cy.get('form[data-provider="windowslive"]').submit()
})

cy.origin(
'login.live.com',
{
args: {
username,
password,
},
},
({ username, password }) => {
cy.get('input[type="email"]').type(username)
cy.get('input[type="submit"]').click()
cy.get('input[type="password"]').type(password, {
log: false,
})
cy.get('input[type="submit"]').click()
cy.get('#idBtn_Back').click()
}
)

cy.get('h6.dropdown-header').should('contain', name)
}

组合登录命令

我们可以将这些函数组合成单个命令:

cypress/support/commands.ts
Cypress.Commands.add(
'loginToAuth0ViaSocial',
(SOCIAL_PROVIDER: 'microsoft' | 'google' | 'facebook') => {
const log = Cypress.log({
displayName: 'Social LOGIN',
message: [`🔐 Authenticating | ${SOCIAL_PROVIDER}`],
// @ts-ignore
autoEnd: false,
})
log.snapshot('before')

switch (SOCIAL_PROVIDER) {
case 'microsoft':
logIntoMicrosoft(
Cypress.env('MICROSOFT_USERNAME'),
Cypress.env('MICROSOFT_PASSWORD'),
Cypress.env('MICROSOFT_NAME')
)
break
case 'google':
logIntoGoogle(
Cypress.env('GOOGLE_USERNAME'),
Cypress.env('GOOGLE_PASSWORD'),
Cypress.env('GOOGLE_NAME')
)
break
case 'facebook':
logIntoFacebook(
Cypress.env('FACEBOOK_USERNAME'),
Cypress.env('FACEBOOK_PASSWORD'),
Cypress.env('FACEBOOK_NAME')
)
break
default:
throw new Error('no social provider configured!')
}

log.snapshot('after')
log.end()
}
)

现在可以在测试中使用loginToAuth0ViaSocial命令。以下是通过Auth0登录用户并运行基本检查的测试:

auth.cy.js
describe('社交登录演示', () => {
beforeEach(() => {
// 此处可提供facebook、google或microsoft
cy.loginToAuth0ViaSocial('facebook')
cy.visit('http://localhost:3000/')
})

it('导航到应用后显示示例项目标题', () => {
cy.get('h1').should('contain', 'React.js示例项目')
})
})

以下是三个提供商依次运行的示例:

最后,我们可以重构登录命令,利用cy.session()存储已登录用户,避免每次测试前重新认证。这也大大降低了因频繁认证尝试导致账号被封的风险。可以适当优化代码!

cypress/support/commands.ts
Cypress.Commands.add(
'loginToAuth0ViaSocial',
(SOCIAL_PROVIDER: 'microsoft' | 'google' | 'facebook') => {
const log = Cypress.log({
displayName: 'Social LOGIN',
message: [`🔐 Authenticating | ${SOCIAL_PROVIDER}`],
// @ts-ignore
autoEnd: false,
})
log.snapshot('before')

cy.session(
`social-${SOCIAL_PROVIDER}`,
() => {
switch (SOCIAL_PROVIDER) {
case 'microsoft':
logIntoMicrosoft(
Cypress.env('MICROSOFT_USERNAME'),
Cypress.env('MICROSOFT_PASSWORD'),
Cypress.env('MICROSOFT_NAME')
)
break
case 'google':
logIntoGoogle(
Cypress.env('GOOGLE_USERNAME'),
Cypress.env('GOOGLE_PASSWORD'),
Cypress.env('GOOGLE_NAME')
)
break
case 'facebook':
logIntoFacebook(
Cypress.env('FACEBOOK_USERNAME'),
Cypress.env('FACEBOOK_PASSWORD'),
Cypress.env('FACEBOOK_NAME')
)
break
default:
throw new Error('no social provider configured!')
}
},
{
validate: () => {
cy.visit('http://localhost:3000')
switch (SOCIAL_PROVIDER) {
case 'microsoft':
cy.get('h6.dropdown-header').should(
'contain',
Cypress.env('MICROSOFT_NAME')
)
break
case 'google':
cy.get('h6.dropdown-header').should(
'contain',
Cypress.env('GOOGLE_NAME')
)
break
case 'facebook':
cy.get('h6.dropdown-header').should(
'contain',
Cypress.env('FACEBOOK_NAME')
)
break
default:
throw new Error('no social provider configured!')
}
},
}
)

log.snapshot('after')
log.end()
}
)

通过使用cy.session(),我们的测试现在应该运行得更快!

希望本指南能帮助您顺利使用cy.origin()cy.session()。如果在遵循本指南时遇到任何问题或有任何反馈,请提交Github issue。祝测试愉快!

另请参阅