-
-
Save csuzw/845b589549b61d3a5fe18e49592e166f to your computer and use it in GitHub Desktop.
// This goes in cypress/plugins/index.js | |
const AzureAdSingleSignOn = require('./azure-ad-sso/plugin').AzureAdSingleSignOn | |
module.exports = (on, config) => { | |
on('task', {AzureAdSingleSignOn:AzureAdSingleSignOn}) | |
} |
// This is an example of how you might use the plugin in your tests | |
describe('My spec', function() { | |
Cypress.Commands.add('setCookies', function () { | |
const options = { | |
username: Cypress.env('username'), | |
password: Cypress.env('password'), | |
loginUrl: Cypress.env('appUrl'), | |
postLoginSelector: '#myselector', | |
headless: true, | |
logs: false | |
} | |
cy.task('AzureAdSingleSignOn', options).then(result => { | |
cy.clearCookies() | |
result.cookies.forEach(cookie => { | |
cy.setCookie(cookie.name, cookie.value, { | |
domain: cookie.domain, | |
expiry: cookie.expires, | |
httpOnly: cookie.httpOnly, | |
path: cookie.path, | |
secure: cookie.secure | |
}) | |
Cypress.Cookies.preserveOnce(cookie.name) | |
}) | |
}) | |
}) | |
before(function() { | |
cy.setCookies(); | |
}) | |
it('Visits the site as logged in user', function() { | |
cy.visit(Cypress.env('appUrl')); | |
cy.contains(`Hello, ${Cypress.env('username')}!`) | |
}) | |
}) |
// I put this in cypress/plugins/azure-ad-sso directory | |
'use strict' | |
const puppeteer = require('puppeteer') | |
/** | |
* | |
* @param {options.username} string username | |
* @param {options.password} string password | |
* @param {options.loginUrl} string password | |
* @param {options.postLoginSelector} string a selector on the app's post-login return page to assert that login is successful | |
* @param {options.headless} boolean launch puppeteer in headless more or not | |
* @param {options.logs} boolean whether to log cookies and other metadata to console | |
* @param {options.getAllBrowserCookies} boolean whether to get all browser cookies instead of just for the loginUrl | |
*/ | |
module.exports.AzureAdSingleSignOn = async function AzureAdSingleSignOn(options = {}) { | |
validateOptions(options) | |
const browser = await puppeteer.launch({ headless: !!options.headless }) | |
const page = await browser.newPage() | |
await page.goto(options.loginUrl) | |
await typeUsername({ page, options }) | |
await typePassword({ page, options }) | |
const cookies = await getCookies({ page, options }) | |
await finalizeSession({ page, browser, options }) | |
return { | |
cookies | |
} | |
} | |
function validateOptions(options) { | |
if (!options.username || !options.password) { | |
throw new Error('Username or Password missing for login') | |
} | |
if (!options.loginUrl) { | |
throw new Error('Login Url missing') | |
} | |
if (!options.postLoginSelector) { | |
throw new Error('Post login selector missing') | |
} | |
} | |
async function typeUsername({ page, options } = {}) { | |
await page.waitForSelector('input[name=loginfmt]:not(.moveOffScreen)', { visible: true, delay: 10000 }) | |
await page.type('input[name=loginfmt]', options.username, { delay: 50 }) | |
await page.click('input[type=submit]') | |
} | |
async function typePassword({ page, options } = {}) { | |
await page.waitForSelector('input[name=Password]:not(.moveOffScreen),input[name=passwd]:not(.moveOffScreen)', { visible: true, delay: 10000 }) | |
await page.type('input[name=passwd]', options.password, { delay: 50 }) | |
await page.click('input[type=submit]') | |
} | |
async function getCookies({ page, options } = {}) { | |
await page.waitForSelector(options.postLoginSelector, { visible: true, delay: 10000 }) | |
const cookies = options.getAllBrowserCookies | |
? await getCookiesForAllDomains(page) | |
: await page.cookies(options.loginUrl) | |
if (options.logs) { | |
console.log(cookies) | |
} | |
return cookies | |
} | |
async function getCookiesForAllDomains(page) { | |
const cookies = await page._client.send('Network.getAllCookies', {}) | |
return cookies.cookies | |
} | |
async function finalizeSession({ page, browser, options } = {}) { | |
await browser.close() | |
} |
Great work!!
I modified it slightly to connect to the same browser instance as Cypress. Avoids having to copy over the cookies
https://gist.github.com/rahulpnath/4362ff2226ea36e056784f92c0d64434
Great work!!
I modified it slightly to connect to the same browser instance as Cypress. Avoids having to copy over the cookies
https://gist.github.com/rahulpnath/4362ff2226ea36e056784f92c0d64434
Thanks @rahulpnath, I didn't know you could do this, will give it a try at some point.
@csuzw I tried on the new edge(chromium) one where it worked. Let know how it goes.
Great work!!
I modified it slightly to connect to the same browser instance as Cypress. Avoids having to copy over the cookies
https://gist.github.com/rahulpnath/4362ff2226ea36e056784f92c0d64434
So if I copy your code from link above and update data then I should login using azure ad?
Works great when cypress app is open.
But when running cypress run
in command-line it seems to not set/preserve the cookie it seems like and hence my oauth login url then redirects to login.microsoft.com throwing entire test off and errors out.
This definitely use to work with cypress run
as I had a working build pipeline with it. There seem to be so many slightly different flavours of this issue though that it's hard to say what might be wrong. It's also possible something has changed with Cypress since I did this - I haven't really looked at it this since I created it.
Setting getAllBrowserCookies
to true fixed it! @csuzw
Thanks for your prompt response!
Is there a way to get all the keys and values from local storage instead of the cookies? I'm getting "localStorage is not defined"
The issue I am facing now is that I am getting a 302 in Cypress when trying to run this. Puppeteer fills out the Azure information, and successfully logs in, but when trying to open the desired page in Cypress, I get the 302. Has anybody had issues with this? @csuzw?
I am also facing this issue as @WvdE described. its successfully logged in using puppeteer and giving me cypresserror:cy.task('AzureAdSingleSignOn') timed out after waiting 60000ms. Could you please help
cy.task("azureSingleSignOn", options, { timeout: 200000 }).then(({ cookies }) => {
cy.log(cookies);
console.log(cookies);
cookies.forEach((cookie) => {
if (cookie) {
cy.setCookie(cookie.name, cookie.value, {
domain: cookie.domain,
expiry: cookie.expires,
httpOnly: cookie.httpOnly,
path: cookie.path,
secure: cookie.secure,
sameSite: "lax",
});
Cypress.Cookies.defaults({ preserve: cookie.name });
}
});
Why after set cookies and preserve that, When I tried cy.visit("/") its again going to sso azure login page? When I debug I saw cookies are remove automatically. What is the issue?
Able to run. connect puppeteer method. Launch method giving cookies but cypress remove those cookies on cy.visit("/").
const browser = await puppeteer.connect({
browserURL: `http://localhost:${debuggingPort}`,
defaultViewport: null,
});
Any updates on how this works with the new cy.session()
? I'm struggling to get it working because Cypress.Cookies.defaults
got removed.
Awesome work @ddregalo,
Always feels good when you get your code working.
Yeah, I'd starting getting a nice collection of tests going and see how that works. My issue started (the instability) once I started having multiple separate spec files, and the cookies were being cleared between them.