cypress-social-logins
Cypress身份验证流程使用社交网络提供商
** ⚠️免责声明和限制⚠️ **
这个插件在CI环境中不能很好地工作由于谷歌、GitHub等采用的反欺诈检测机制,为什么?如果您尝试从具有不同ip、地理位置和其他指纹识别的CI机器登录,而您使用的帐户通常不会尝试登录,那么这将触发多因素身份验证、CAPTCHA或其他确认身份的方法。当需要这些额外的步骤时,这个插件就不能很好地工作了。
关于
这个Cypress库可以为GitHub,谷歌或Facebook等服务执行第三方登录(想想oauth)。
它通过将登录过程委托给 puppeteer 流执行登录并为测试中的应用程序返回cookie,因此在测试期间,调用Cypress流可以设置cookie。
支持
支持的身份提供者:
| 提供者 | 插件名称 |
|---|---|
| 谷歌 | GoogleSocialLogin |
| GitHub | GitHubSocialLogin |
| 微软 | MicrosoftSocialLogin |
| 亚马逊 | AmazonSocialLogin |
| 脸谱网 | FacebookSocialLogin |
| 推特 | TBD |
| TBD |
用法
- 使用一组用于社交登录流交互的选项调用声明的任务
- 在的帮助下为测试流设置cookie
Cypress.Cookies.defaults - 从puppeteer复制全部或部分(或没有)本地和会话存储对象到本地实例。 注意: 如果希望将localStorage持久化通过所有测试,请参见 localStorage故障排除 下面。
cy.clearCookies()
return cy.task('GoogleSocialLogin', socialLoginOptions).then(results => {
results['cookies'].forEach(cookie => {
if (cookie.domain.includes(cookieName)) {
cy.setCookie(cookie.name, cookie.value, {
domain: cookie.domain,
expiry: cookie.expires,
httpOnly: cookie.httpOnly,
path: cookie.path,
secure: cookie.secure
})
}
})
cy.window().then(window => {
Object.keys(results.ssd).forEach(key => window.sessionStorage.setItem(key, results.ssd[key]))
Object.keys(results.lsd).forEach(key => window.localStorage.setItem(key, results.lsd[key]))
})
})
传递给任务的选项包括:
| 选项名称 | 描述 | 例子 |
|---|---|---|
| 用户名 | ||
| 密码 | ||
| loginUrl | 包含社交网络按钮的登录页面的URL | https://www.example.com/login |
| loginUrlCredentials | 的基本身份验证凭据 loginUrl | {username: user, password: demo} |
| arg游戏 | 允许为puppeteer提供进一步参数的字符串数组 | ['--no-sandbox', '--disable-setuid-sandbox'] |
| 无头 | 是否在无头模式下运行木偶 | 真正的 |
| 日志 | 是否记录与loginUrl网站和cookie数据的交互 | 假 |
| loginSelector | 页面上的选择器,它定义了要使用的特定社交网络,并且可以单击,例如按钮或链接 | 'a[href="/auth/auth0/google-oauth2"]' |
| postLoginSelector | 登录后页面上的选择器,可以断言该选择器以确认成功登录 | '.account-panel' |
| preLoginSelector | 一个选择器,在单击登录按钮之前查找并单击它(对于接受cookie很有用) | '.ind-cbar-right button' |
| preLoginSelectorIframe | 字符串选择器为preLoginSelector找到iframe | 'div#consent iframe' |
| preLoginSelectorIframeDelay | 数字延迟一个特定的毫秒后,单击preLoginSelector。传递一个false (false, 0, null, undefined, ")来完全避免。 | 2000 |
| otpSecret | 基于OTPLIB生成一次性密码的秘密 | 'SECRET' |
| loginSelectorDelay | 在点击登录按钮之前延迟一定的时间,默认为250ms。传递一个布尔值false来完全避免。 | 100 |
| getAllBrowserCookies | 是否获取所有浏览器cookie,而不仅仅是那些具有loginUrl域的cookie | 真正的 |
| isPopup | 布尔值,是像弹出窗口一样显示的谷歌验证 | 真正的 |
| popupDelay | 数字,延迟特定毫秒之前弹出显示。传递一个false (false, 0, null, undefined, ")来完全避免 | 2000 |
| cookieDelay | 数字,在获得cookie之前延迟一个特定的毫秒。传递一个false (false, 0, null,undefined, ")来完全避免 | 100 |
| postLoginClick | 可选:一个选择器,在单击登录按钮后查找并单击 | #idSIButton9 |
| usernameField | CustomizedLogin所需:字符串,用户名字段的选择器 | |
| usernameSubmitBtn | CustomizedLogin可选:string,用户名按钮的选择器 | |
| passwordField | CustomizedLogin所需:字符串,密码字段的选择器 | |
| passwordSubmitBtn | CustomizedLogin可选:string,密码提交按钮的选择器 | |
| screenshotOnError | 可选:如果用户名,密码或登录后页面出现错误,将抓取屏幕截图并保存在Cypress屏幕截图文件夹中。 | 假 |
| additionalSteps | 可选:函数,定义执行用户名和密码函数后可能需要的任何附加步骤,例如回答安全问题,PIN或在用户名和密码处理后可能需要填写的任何内容。这个函数和属性必须从index.js的Cypress Plugins目录中定义或引用。 | async function moreSteps({page, options} = {}) { await page.waitForSelector('#pin_Field') await page.click('#pin_Field') } |
| trackingConsentSelectors | 可选:在点击登录按钮后,但在第三方网站上输入详细信息之前,查找并点击选择器(用于接受第三方cookie,例如Facebook登录)。提供多个,如果想要只接受必要的cookie,它需要多次点击 | ['button[data-testid="cookie-policy-dialog-manage-button"]', 'button-data-testid="cookie-policy-manage-dialog-accept-button"]'] |
| preVisitLoginUrlSetCookies | 可选:访问前要设置的cookie数组 loginUrl | [{name: 'enable-social-login', value: 'true', domain: '.cypress.io'}] |
安装
将插件作为依赖项安装
npm install --save-dev cypress-social-logins
导入插件
导入 cypress-social-logins 插件定义为特定的社交
您感兴趣的网络登录,并声明一个执行
登录。
例子:
const {GoogleSocialLogin} = require('cypress-social-logins').plugins
module.exports = (on, config) => {
on('task', {
GoogleSocialLogin: GoogleSocialLogin
})
}
使用社交登录
一旦定义了Cypress任务,我们就可以公开使用的测试用例 它。任务将接受一个选项对象,其中包含用户名、密码和 需要指定的其他配置,以便任务可以导航 正确地浏览页面。
任务完成后,它将返回来自新对象的cookie列表
页面。最有可能的是,这些cookie需要设置为剩下的时间
会话在测试流中,因此示例代码显示的情况
Cypress.Cookies.defaults。
describe('Login', () => {
it('Login through Google', () => {
const username = Cypress.env('googleSocialLoginUsername')
const password = Cypress.env('googleSocialLoginPassword')
const loginUrl = Cypress.env('loginUrl')
const cookieName = Cypress.env('cookieName')
const socialLoginOptions = {
username: username,
password: password,
loginUrl: loginUrl,
headless: true,
logs: false,
loginSelector: '[href="/auth/auth0/google-oauth2"]',
postLoginSelector: '.account-panel'
}
return cy.task('GoogleSocialLogin', socialLoginOptions).then(({cookies}) => {
cy.clearCookies()
const cookie = cookies.filter(cookie => cookie.name === cookieName).pop()
if (cookie) {
cy.setCookie(cookie.name, cookie.value, {
domain: cookie.domain,
expiry: cookie.expires,
httpOnly: cookie.httpOnly,
path: cookie.path,
secure: cookie.secure
})
Cypress.Cookies.defaults({
preserve: cookieName
})
}
})
})
})
定义自定义登录
1选择
当你需要使用这个插件不支持的社交登录时,你可以使用 baseLoginConnect() 函数被导出为插件的一部分,如下所示:
const {baseLoginConnect} = require('cypress-social-logins').plugins
module.exports = (on, config) => {
on('task', {
customLogin(options) {
async function typeUsername({page, options} = {}) {
await page.waitForSelector('input[id="username"]')
await page.type('input[id="username"]', options.username)
}
async function typePassword({page, options} = {}) {
await page.waitForSelector('input[id="password"]')
await page.type('input[id="password"]', options.password)
await page.click('button[id="_submit"]')
}
return baseLoginConnect(typeUsername, typePassword, null, options)
}
})
}
2选择
你也可以用 CustomizedLogin 函数中提供选择器 options 对象传递给函数。属性 usernameField 和 passwordField 是必需的,否则该函数将抛出一个Error,并带有一条关于需求的消息。属性 usernameSubmitBtn 和 passwordSubmitBtn 是可选的。(建议定义passwordSubmitBtn以帮助进行登录流程。)
测试文件-
describe('Login', () => {
it('Login through Google', () => {
const username = Cypress.env('googleSocialLoginUsername')
const password = Cypress.env('googleSocialLoginPassword')
const loginUrl = Cypress.env('loginUrl')
const cookieName = Cypress.env('cookieName')
const socialLoginOptions = {
username,
password,
loginUrl,
usernameField: '#input_username',
passwordField: '#input_password',
passwordSubmitBtn: '#login_btn_sign',
headless: true,
logs: false,
loginSelector: '[href="/auth/auth0/google-oauth2"]',
postLoginSelector: '.account-panel'
}
return cy.task('GoogleSocialLogin', socialLoginOptions).then(({cookies}) => {
cy.clearCookies()
const cookie = cookies.filter(cookie => cookie.name === cookieName).pop()
if (cookie) {
cy.setCookie(cookie.name, cookie.value, {
domain: cookie.domain,
expiry: cookie.expires,
httpOnly: cookie.httpOnly,
path: cookie.path,
secure: cookie.secure
})
Cypress.Cookies.defaults({
preserve: cookieName
})
}
})
})
})
Plugns -
/**
* @type {Cypress.PluginConfig}
*/
const {CustomizedLogin} = require('cypress-social-logins').plugins
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
on('task', {
customizedLogin: options => {
return CustomizedLogin(options)
}
})
}
使用AmazonSocialLogin与OneTimePassword
你需要一个激活了2fa的亚马逊账户。qr码由Amazon提供,包含一个SECRET 计算OTP。这是强制性的,因为新亚马逊账户的2fa强制执行。不支持短信或电子邮件。 你可以从qr码中提取秘密:
otpauth://totp/Amazon%3ASomeUser%40Example?secret=IBU3VLM........&issuer=Amazon
您需要在Amazon中使用GoogleAuthenticator或任何支持OTP的密码管理器设置帐户。进一步 信息: https://www.amazon.com/gp/help/customer/display.html?nodeId=GE6SLZ5J9GCNRW44
向登录工作流添加附加步骤
如果在提交用户名和密码后,您的登录工作流还有更多的步骤,您可以为这些额外的步骤定义函数,然后将它们分配给 options.additionalSteps 属性在Cypress插件文件。
/**
* @type {Cypress.PluginConfig}
*/
async function fewMoreSteps({page, options} = {}) {
// ... define steps
}
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
on('task', {
customizedLogin: options => {
options.additionalSteps = fewMoreSteps
return CustomizedLogin(options)
}
})
}
定义自定义登录
当你需要使用这个插件不支持的社交登录时,你可以使用 baseLoginConnect() 函数被导出为插件的一部分,如下所示:
const {baseLoginConnect} = require('cypress-social-logins').plugins
module.exports = (on, config) => {
on('task', {
customLogin(options) {
async function typeUsername({page, options} = {}) {
await page.waitForSelector('input[id="username"')
await page.type('input[id="username"', options.username)
}
async function typePassword({page, options} = {}) {
await page.waitForSelector('input[id="password"]')
await page.type('input[id="password"]', options.password)
await page.click('button[id="_submit"]')
}
return baseLoginConnect(typeUsername, typePassword, null, options)
}
})
}
使用AmazonSocialLogin与OneTimePassword
你需要一个激活了2fa的亚马逊账户。qr码由Amazon提供,包含一个SECRET 计算OTP。这是强制性的,因为新亚马逊账户的2fa强制执行。不支持短信或电子邮件。 你可以从qr码中提取秘密:
otpauth://totp/Amazon%3ASomeUser%40Example?secret=IBU3VLM........&issuer=Amazon
您需要在Amazon中使用GoogleAuthenticator或任何支持OTP的密码管理器设置帐户。进一步 信息: https://www.amazon.com/gp/help/customer/display.html?nodeId=GE6SLZ5J9GCNRW44
故障排除
尝试输入用户名时超时
在实例化插件时,请确保在选项中为插件提供了用户名或密码。如果你通过环境变量传递它,那么插件会寻找这两个: CYPRESS_googleSocialLoginUsername 和 CYPRESS_googleSocialLoginPassword
如果您的应用程序使用弹出式授权,请确保提供 isPopup: true 配置参数。
选择器超时错误
操纵木偶的人使用 document.querySelectors. 如果您使用jQuery之类的选择器,您可能会面临超时错误,因为Puppeteer可能无法理解。
您可以查看这些链接以获得有效选择器的示例: document.querySelector () CSS选择器
启动浏览器进程失败
如果在Linux服务器上出现错误,例如:
Error: Failed to launch the browser process!
[768:768:0423/165641.025850:ERROR:zygote_host_impl_linux.cc(89)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.
TROUBLESHOOTING:
你应该传递这个参数 --no-sandbox 作为附加参数传递给插件。
localStorage没有在所有测试中持久化
如果你发现 lsd 如果没有通过测试(如果您需要一个来自SSO的JWT以便在每次测试之前登录)使用上面的默认实现,那么您可以使用包吗 cypress-localstorage-commands (https://www.npmjs.com/package/cypress-localstorage-commands).
使用方法:
npm install --save-dev cypress-localstorage-commands
import 'cypress-localstorage-commands'
before(() => {
describe('Login through Google', () => {
const username = Cypress.env('googleSocialLoginUsername')
const password = Cypress.env('googleSocialLoginPassword')
const loginUrl = Cypress.env('loginUrl')
const localStorageItem = Cypress.env('lsdItemName')
const socialLoginOptions = {
username: username,
password: password,
loginUrl: loginUrl,
headless: true,
logs: false,
loginSelector: '[href="/auth/auth0/google-oauth2"]',
postLoginSelector: '.account-panel'
}
// Clears localStorage prior to getting any new localStorage items
cy.clearLocalStorageSnapshot()
return cy.task('GoogleSocialLogin', socialLoginOptions).then(({lsd}) => {
// Check for localStorage item, such as a JWT or similar
const hasLsd = Object.keys(lsd)
.filter(item => item === localStorageItem)
.pop()
if (hasLsd) {
cy.window().then(() => {
Object.keys(lsd).forEach(key => {
cy.setLocalStorage(key, lsd[key])
})
})
// Saves a snapshot of localStorage
cy.saveLocalStorage()
}
})
})
})
// Restore the saved localStorage snapshot prior to each test
beforeEach(() => {
cy.restoreLocalStorage()
})
// Save the localStorage snapshot after each test
afterEach(() => {
cy.saveLocalStorage()
})
错误:从文件中找不到模块:“ws”
如果您收到如下错误信息:
Error: module not found: "ws" from file ..... node_modules/puppeteer/lib/WebSocketTransport.js #17
这可能是由于您需要导出的插件函数之一,例如 GoogleSocialLogin 在您的规范文件中,除了需要它之外 cypress/plugins/index.js. 从您的规范文件中删除它,或者从 support/index.js 确保你导出了 GoogleSocialLogin 函数作为任务只从 /plugins/index.js 文件。
参见关于 本期。
不接受Amazon OTP
请注意你的机器上的正确时间。确保您正在使用ntp同步。
附加步骤不是函数
请避免在测试文件中定义额外的steps函数。它将导致错误,当您通过 options 对象通过 cy.task()。
如果您也有多个场景的情况,例如两种情况都需要在密码后输入PIN或安全性,或者无需额外步骤即可输入通常的用户名和密码登录流,则可以在 options 对象作为希望应用哪些附加函数的指示符。
例子:
/**
* @type {Cypress.PluginConfig}
*/
async function fewMoreStepsPin({page, options} = {}) {
// ... define steps to enter PIN
}
async function fewMoreStepsSecurityQ({page, option} = {}) {
// ... define steps to enter secuirty question
}
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 === 'pin') {
// assign options.addtionalSteps pin function
options.additionalSteps = fewMoreStepsPin
} else if (options.moreSteps === 'securityQ') {
// assign options.additionalSteps securityQ
options.additionalSteps = fewMoreStepsSecurityQ
}
return CustomizedLogin(options)
}
})
}
文档
- 项目文档 —开发、测试、架构和约定。
贡献
请咨询 贡献 有关为本项目做出贡献的指导方针。
作者
Liran塔尔 [email protected]