Running a second Puppeteer script using the same session cookies

It is possible to first run a Puppeteer script that logins into Canvas, then save the session cookies in a file. Now a second script can be run that loads these cookies and proceeds to do some action - all without having to log in again.

The first script is:


const puppeteer = require('puppeteer');
const fs = require('fs');
const config = require('./config.json'); // contains username and password to use

// const delay = ms => new Promise(res => setTimeout(res, ms));
const course_id = process.argv[2];
if (!course_id) {
    throw "Please provide a course_id as the first argument";
}
console.log("course_id is ", course_id);

const login_url='http://'+config.canvas.host+'/login/canvas';
console.info("login_url is ", login_url);


(async() => {
    const browser = await puppeteer.launch({headless: false, userDataDir: './puppeteer_data' });
    const page = await browser.newPage();
    await page.setViewport({
	width: 1280,
	height: 1024,
	deviceScaleFactor: 1,
    });
    await page.goto(login_url, {waitUntil: 'load'});
    console.log(page.url());

    // Type our username and password
    await page.type('#pseudonym_session_unique_id', config.canvas.username); // it you pass a third argument {delay: 100} - it will be similar to a user typing
    await page.type('#pseudonym_session_password', config.canvas.password);

    // Submit form
    await page.click('.Button.Button--login');

    //delay(3000);
    page.waitFor(3000); // the Dashboard does not render correctly in my Canvas instance, hence just wait and then go elsewhere

    // Go to the page for Question banks and Wait for the page to load
    qb_url='http://'+config.canvas.host+'/courses/'+course_id+'/question_banks'
    console.log("qb_url is ", qb_url);
    await page.goto(qb_url, {waitUntil: 'load'});
    console.log('FOUND!', page.url());
    
    const cookies = await page.cookies()
    console.info("cookies are ", cookies);

    fs.writeFile('canvas-session.json', JSON.stringify(cookies, null, 2), function(err) {
        if (err) throw err;
        console.log('completed write of cookies');
    });



    const token = await page.evaluate(() => {
	const token1=document.querySelector("#edit_bank_form input[name=authenticity_token]").value // output the token just to see it
	return token1;
    });
    console.info("token", token);

    browser.close();

})();

The second script is:


const puppeteer = require('puppeteer');
const fs = require('fs');
const config = require('./config.json'); // contains username and password to use

// const delay = ms => new Promise(res => setTimeout(res, ms));
const course_id = process.argv[2];
if (!course_id) {
    throw "Please provide a course_id as the first argument";
}
console.log("course_id is ", course_id);


(async() => {
    const cookiesString = fs.readFileSync('./canvas-session.json', 'utf8');
    //console.log("cookiesString are ", cookiesString);
    const cookies = JSON.parse(cookiesString);
    //console.log("cookies are ", cookies);

    process.on('unhandledRejection', (reason, p) => {
	console.error('Unhandled Rejection at: Promise', p, 'reason:', reason);
    });

    const browser = await puppeteer.launch({headless: false,
					    userDataDir: '/home/maguire/puppeteer/puppeteer_data'
					   });


    const page = await browser.newPage();
    await page.setViewport({
	width: 1280,
	height: 1024,
	deviceScaleFactor: 1,
    });
    
    page.waitFor(1000);
    console.info("setting cookies")
    await page.setCookie.apply(page, cookies);


    // Go to the page for Question banks and Wait for the page to load
    qb_url='http://'+config.canvas.host+'/courses/'+course_id+'/question_banks'
    console.log("qb_url is ", qb_url);
    await page.goto(qb_url, {waitUntil: 'load'});
    console.log('FOUND!', page.url());
    
    const token = await page.evaluate(() => {
	const token1=document.querySelector("#edit_bank_form input[name=authenticity_token]").value // output the token just to see it
	return token1;
    });
    console.info("token", token);

    const title="A new and interesting question bank"
    await page.$eval('#edit_bank_form #assessment_question_bank_title', (el, _title) => el.value = _title, title); // output the token just to see it

    const f1 = await page.$eval('form#edit_bank_form', form => form.submit());
    page.waitFor(3000);
    console.info("f1", f1);


    await page.screenshot({path: 'login4.png'});

    // Extract the results from the page
    const links = await page.evaluate(() => {
	let questionBanks = [];	// this will collect the question bank informatio

	const anchors = Array.from(document.querySelectorAll('.question_bank'));
	anchors.forEach(function(anchor) {
	    let jsonObject = {
		id: anchor.id,
		title: anchor.querySelector('.title').text,
		href: anchor.querySelector('.title').href,
            };
            questionBanks.push(jsonObject);
	});

	return questionBanks;
	//return anchors.map(anchor => anchor.id+','+anchor.querySelector('.title').href+','+anchor.querySelector('.title').text); // textContent
    });
    //console.log(links.join('\n'));

    fs.writeFile('results5.json', JSON.stringify(links), function(err) {
        if (err) throw err;
        console.log('completed writing JSON information about question banks');
    });

    browser.close();

})();

Note that if already have a chromium running a Puppeteer script and you have non-headless mode set


    const browser = await puppeteer.launch({headless: false,
					    userDataDir: '/home/maguire/puppeteer/puppeteer_data'
					   });

Then you will get the following error:

Unhandled Rejection at: Promise Promise {
   Error: Failed to launch chrome!


TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md

    at onClose (/home/maguire/node_modules/puppeteer/lib/Launcher.js:340:14)
    at ChildProcess.helper.addEventListener (/home/maguire/node_modules/puppeteer/lib/Launcher.js:330:60)
    at emitTwo (events.js:131:20)
    at ChildProcess.emit (events.js:214:7)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:198:12) } reason: Error: Failed to launch chrome!

Interestingly it will work when you use headless mode:


    const browser = await puppeteer.launch({headless: true,
					    userDataDir: '/home/maguire/puppeteer/puppeteer_data'
					   });

Note that the above scripts used the userDataDir: './puppeteer_data' option to the launch, causing the scripts to run with the same user data. However, it should be noted that this is not required, as can easily be seen by commenting out the argument in the 2nd script. The only thing necessary to have the second script run without another login is the cookies.