Maybe I'm sleepy, but under what circumstances would the following occur?
let foo;
page
.evaluate(() => {
// return works... but not closure assignment
// doesn't work
foo = 'foo';
// works
return 'bar';
})
.then(bar => {
console.log('foobar', foo, bar);
// > foobar undefined bar
});
This is occurring in a mocha test using puppeteer
update: the exact entire code
node 9.11.2
/* global describe, it, before, after */
const fs = require('fs-extra');
const path = require('path');
const assert = require('assert');
const puppeteer = require('puppeteer');
const sleep = require('shleep');
const extPath = path.resolve(__dirname, '..', 'build');
const { name } = fs.readJSONSync(path.resolve(extPath, 'manifest.json'));
// Access chrome object in Extensions
// https://github.com/GoogleChrome/puppeteer/issues/2878
describe('chrome extension', () => {
let browser;
let extensionPage;
before(async function() {
this.timeout(90 * 1000);
// start puppeteer
browser = await puppeteer.launch({
headless: false,
args: [
`--disable-extensions-except=${extPath}`,
`--load-extension=${extPath}`
]
});
// poll instead of hope this is enough time?
const EXT_LOAD_DELAY = 100;
await sleep(EXT_LOAD_DELAY);
const targets = await browser.targets();
const extensionTarget = targets.find(
({ _targetInfo }) =>
_targetInfo.type === 'background_page' && _targetInfo.title === name
);
const page = await extensionTarget.page();
let foo;
page
.evaluate(() => {
// return works... but not closure assignment
// doesn't work
foo = 'foo';
// doesn't log
console.log('foo', foo);
// works
return 'bar';
})
.then(bar => {
console.log('foobar', foo, bar);
// > foobar undefined bar
});
});
it('should load', async () => {
assert(true);
});
});
screenshot of test
The evaluate
method in puppeteer has no concept of variables that you have declared in your code either locally or globally. The function that you pass to the evaluate
method are functions that are to be executed in the page context (ie. in your browser). Since foo
is not declared in the page context, it cannot access it and therefore cannot update its value.
So to step through your code:
let foo;
await page.evaluate(() => {
foo = 'foo'; // Since no variable foo is known to the evaluate method in the context of your page
// this line , effectively, assigns a new variable called foo with a value 'foo' which
// is then lost once the method has completed since its value is never returned.
return 'bar'; // This value is returned from the method and is therefore retained below
})
.then(bar => {
console.log('foobar', foo, bar);
// foobar is output as expected since you've hardcoded it
// foo is now referring to the global foo you declared earlier but since you have used `let` and not
// assigned it any value, it is output as 'undefined'
// bar is the value you returned from your 'evaluate' function and is therefore output as 'bar' as
// expected in the console log.
});
If you wanted to use the evaluate
to update your variable foo
you would have to do it like this:
let foo;
foo = await page.evaluate(() => {
return 'foo'
});
console.log(foo); // Now outputs 'foo' as expected
You can, however, inject variables into evaluate
methods and update their values (if you wish), for example:
let foo = 'foo'
console.log(foo); // Outputs 'foo' as expected
foo = await page.evaluate((injectedFoo) => {
return `new${injectedFoo}`;
}, foo);
console.log(foo); // Outputs 'newfoo'
So what happens here is you've injected the variable foo
into the evaluate
method by passing it as an argument at the end of the method declaration. The evaluate
method now contains a variable (which I've called injectedFoo
for clarity) which carries the original value of the foo
variable.
I'm then returning the string new
appended to the beginning to the foo
variable string and output the final value of that in the console.
I hope this helps to explain how the evaluate
method works!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With