Is it possible to use Cypress e2e testing with a firebase auth project?

后端 未结 6 1403
再見小時候
再見小時候 2021-02-19 03:16

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

6条回答
  •  無奈伤痛
    2021-02-19 03:41

    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.

    1. Obtain Firebase SDK localStorage entry via pupeteer
    2. Store the contents in the tmp file (problems passing it via env var to Cypress)
    3. Pass the file location to Cypress via env var and let it read the contents and set the localStorage to setup the session

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

提交回复
热议问题