Marshall Thompson
Marshall Thompson

Reputation: 501

Puppeteer Without an HTML page?

I have successfully made my Puppeteer script work with Highcharts, but only when I goto a page that has the highcharts library script included. I'm trying to figure out how to eliminate the html page requirement for the Puppeteer script. The following highcharts3.html works

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Highcharts Test 3</title>
</head>
<body>
<script src="/lib/highcharts/highcharts.js"></script>
<div id="container" style="width:100%; height:400px;"></div>
</body>
</html>

Here is the script highcharts3.js

const puppeteer = require('puppeteer')
const fs = require('fs')

console.log('main fs W_OK=' + fs.W_OK)

async function run() {

    console.log('run fs W_OK=' + fs.W_OK)

    // const browser = await puppeteer.launch({
    //     headless: true
    // })
    const browser = await puppeteer.launch({
        headless: false,
        slowMo: 2000,
        devtools: true    })

    const page = await browser.newPage()
    page.on("console", msg => console.log(`Page Console: ${msg.text()}`));

    await page.goto('http://localhost:7890/highcharts3.html', {
        waitUntil: "domcontentloaded"
    })

    async function loadChart() {

        console.log('loadChart fs W_OK=' + fs.W_OK)

        await page.evaluate(async (fs) => {

            console.log('page.evaluate fs W_OK=' + fs.W_OK)
            console.log('Highcharts.version='
                + Highcharts.version)

            var myChart = Highcharts.chart('container', {
                chart: {
                    type: 'bar'
                },
                title: {
                    text: 'Fruit Consumption'
                },
                xAxis: {
                    categories: ['Apples', 'Bananas', 'Oranges']
                },
                yAxis: {
                    title: {
                        text: 'Fruit eaten'
                    }
                },
                series: [{
                    name: 'Jane',
                    data: [1, 0, 4]
                }, {
                    name: 'John',
                    data: [5, 7, 3]
                }]
            });
        }, fs)
    }

    await loadChart()
    await browser.close()
}

run()

Now, I want to adapt the above to pull in the highcharts.js file, not via a script include in the html page, but somehow in the puppeteer script itself. Here is my attempt:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Highcharts Test 4</title>
</head>
<body>
<!-- Let's try to do this in the script -->
<!--<script src="/lib/highcharts/highcharts.js"></script>-->
<div id="container" style="width:100%; height:400px;"></div>
</body>
</html>

const puppeteer = require('puppeteer')
const fs = require('fs')
const Highcharts = require('highcharts')

console.log('main fs W_OK=' + fs.W_OK)
console.log('main Highcharts.version=' +
    Highcharts().version)      //Works

async function run() {

    console.log('run fs W_OK=' + fs.W_OK)
    console.log('run Highcharts.version=' + Highcharts().version)   //Works

    // const browser = await puppeteer.launch({
    //     headless: true
    // })
    const browser = await puppeteer.launch({
        headless: false,
        slowMo: 2000,
        devtools: true
    })

    const page = await browser.newPage()
    page.on("console", msg => console.log(`Page Console: ${msg.text()}`));

    await page.goto('http://localhost:7890/highcharts4.html', {
        waitUntil: "domcontentloaded"
    })

    async function loadChart() {

        console.log('loadChart fs W_OK=' + fs.W_OK)
        console.log('loadChart Highcharts.version=' +
            Highcharts().version) //Works

        await page.evaluate(async (Highcharts, fs) => {

            //fs is defined because we passed it to page.evaluate
            console.log('page.evaluate fs W_OK=' + fs.W_OK)

            //The following statement fails with:
            //(node:3580) UnhandledPromiseRejectionWarning:
            // Error: Evaluation failed:
            // TypeError: Highcharts is not a function
            console.log('page.evaluate Highcharts.version=' +
                Highcharts().version)

            //When uncommented in place of the above, fails with:
            //Highcharts is undefined
            //console.log('page.evaluate Highcharts.version='
            // + Highcharts.version)

            var myChart = Highcharts.chart('container', {
                chart: {
                    type: 'bar'
                },
                title: {
                    text: 'Fruit Consumption'
                },
                xAxis: {
                    categories: ['Apples', 'Bananas', 'Oranges']
                },
                yAxis: {
                    title: {
                        text: 'Fruit eaten'
                    }
                },
                series: [{
                    name: 'Jane',
                    data: [1, 0, 4]
                }, {
                    name: 'John',
                    data: [5, 7, 3]
                }]
            });
        }, Highcharts, fs)
    }

    await loadChart()
    await browser.close()
}

run()

This fails in the loadChart function. I can't figure out how to require Highcharts so that it is seen in the scope of the page.

Upvotes: 2

Views: 2420

Answers (1)

Marshall Thompson
Marshall Thompson

Reputation: 501

I finally figured it out, posting in case it will help others. The key was using fs.readFileSync to read in my highcharts.js in the context of the page.

const puppeteer = require('puppeteer')
const fs = require('fs')

async function run() {

    // const browser = await puppeteer.launch({
    //     headless: true
    // })
    const browser = await puppeteer.launch({
        headless: false,
        slowMo: 2000,
        devtools: true
    })

    const page = await browser.newPage()
    page.on("console", msg => console.log(`Page Console: ${msg.text()}`));

    await page.goto('http://localhost:7890/highcharts4.html', {
        waitUntil: "domcontentloaded"
    })

    async function loadChart() {
        //THIS DID THE TRICK!
        page.evaluate(fs.readFileSync('./lib/highcharts/highcharts.js', 'utf8'));

        await page.evaluate(async (fs) => {

            console.log('page.evaluate Highcharts.version='
            + Highcharts.version)

            var myChart = Highcharts.chart('container', {
                chart: {
                    type: 'bar'
                },
                title: {
                    text: 'Fruit Consumption'
                },
                xAxis: {
                    categories: ['Apples', 'Bananas', 'Oranges']
                },
                yAxis: {
                    title: {
                        text: 'Fruit eaten'
                    }
                },
                series: [{
                    name: 'Jane',
                    data: [1, 0, 4]
                }, {
                    name: 'John',
                    data: [5, 7, 3]
                }]
            });
        }, fs)
    }

    await loadChart()
    await browser.close()
}

run()

Now, here is the final version that eliminates the .html page

/**
 * This file creates a highchart, 
 * no html page is required.  The html is crafted
 * within this script.
 */
const puppeteer = require('puppeteer')
const fs = require('fs')

async function run() {

    const browser = await puppeteer.launch({
        headless: true
    })
    // const browser = await puppeteer.launch({
    //     headless: false,
    //     slowMo: 2000,
    //     devtools: true
    // })

    const page = await browser.newPage()
    page.on("console", msg => console.log(`Page Console: ${msg.text()}`));

    const loaded = page.waitForNavigation({
        waitUntil: 'load'
    })

    const html =
        `<!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Highcharts Test 4</title>
        </head>
        <body>
        <div id="container" style="width:100%; height:400px;"></div>
        </body>
        </html>`

    await page.setContent(html)
    await loaded

    async function loadChart() {

        page.evaluate(fs.readFileSync('./lib/highcharts/highcharts.js', 'utf8'));

        await page.evaluate(async (fs) => {

            console.log('page.evaluate Highcharts.version='
                + Highcharts.version)

            var myChart = Highcharts.chart('container', {
                chart: {
                    type: 'bar'
                },
                title: {
                    text: 'Fruit Consumption'
                },
                xAxis: {
                    categories: ['Apples', 'Bananas', 'Oranges']
                },
                yAxis: {
                    title: {
                        text: 'Fruit eaten'
                    }
                },
                series: [{
                    name: 'Jane',
                    data: [1, 0, 4]
                }, {
                    name: 'John',
                    data: [5, 7, 3]
                }]
            });
        }, fs)
    }

    await loadChart()
    await browser.close()
}

run()

Upvotes: 3

Related Questions