I am exploring Cypress for e2e testing, looks like great software. The problem is Authentication, the Cypress documentation explains why using the UI is very bad here.
So
I took the approach of using automated UI to obtain the contents of localStorage used by Firebase JS SDK. I also wanted to do this only once per whole Cypress run so I did it before the Cypress start.
Helper script which obtains contents of localStorage:
const puppeteer = require('puppeteer')
const invokeLogin = async page => {
await page.goto('http://localhost:3000/login')
await page.waitForSelector('.btn-googleplus')
await page.evaluate(() =>
document.querySelector('.btn-googleplus').click())
}
const doLogin = async (page, {username, password}) => {
// Username step
await page.waitForSelector('#identifierId')
await page.evaluate((username) => {
document.querySelector('#identifierId').value = username
document.querySelector('#identifierNext').click()
}, username)
// Password step
await page.waitForSelector('#passwordNext')
await page.evaluate(password =>
setTimeout(() => {
document.querySelector('input[type=password]').value = password
document.querySelector('#passwordNext').click()
}, 3000) // Wait 3 second to next phase to init (couldn't find better way)
, password)
}
const extractStorageEntry = async page =>
page.evaluate(() => {
for (let key in localStorage) {
if (key.startsWith('firebase'))
return {key, value: localStorage[key]}
}
})
const waitForApp = async page => {
await page.waitForSelector('#app')
}
const main = async (credentials, cfg) => {
const browser = await puppeteer.launch(cfg)
const page = await browser.newPage()
await invokeLogin(page)
await doLogin(page, credentials)
await waitForApp(page)
const entry = await extractStorageEntry(page)
console.log(JSON.stringify(entry))
await browser.close()
}
const username = process.argv[2]
const password = process.argv[3]
main({username, password}, {
headless: true // Set to false for debugging
})
Since there were problem with sending JSON as environment variables to Cypress I use tmp file to pass the data between the script and the Cypress process.
node test/getFbAuthEntry ${USER} ${PASSWORD} > test/tmp/fbAuth.json
cypress open --env FB_AUTH_FILE=test/tmp/fbAuth.json
In Cypress I read it from the file system and set it to the localStorage
const setFbAuth = () =>
cy.readFile(Cypress.env('FB_AUTH_FILE'))
.then(fbAuth => {
const {key, value} = fbAuth
localStorage[key] = value
})
describe('an app something', () => {
it('does stuff', () => {
setFbAuth()
cy.viewport(1300, 800)
...