lino-levan / astral

A high-level puppeteer/playwright-like library for Deno
https://jsr.io/@astral/astral
MIT License
248 stars 17 forks source link

`Page.exposeFunction` #96

Open chaosharmonic opened 1 month ago

chaosharmonic commented 1 month ago

Puppeter and Playwright both offer a Page.exposeFunction enabling you to set globals to the window object and call them.

Playwright also has a Page.exposeBinding, though I'm less sure what's distinct there, as well as equivalents to both for the larger BrowserContext.

I'm still having trouble getting function definitions passed in manually from outside, but I can confirm that assigning to window will persist across page.evaluate calls:

await page.evaluate(async () => {
    window['hey'] = () => console.log('listen')
})

await page.evaluate(async () => {
    hey() // will print to browser console
})

That said it's partly an issue getting plain and arrow functions to consistently type coerce to and from strings. The below does work:

const hey = () => {
    console.log('listen!')
}

await page.evaluate(async (hey) => {
    window['listen'] = eval(hey)
}, { args: [ hey.toString() ]})

await page.evaluate(async () => {
    listen() // will still print to browser console
})

But, it only works with arrow functions. Using function -- whether on its own or with const -- you have to isolate the code block and its arguments separately, and then pass them into Function. For a simple example without arguments:

function hey() // or const hey = function...
    console.log('listen!')
}

const hello = `{${hey.toString().split('{').slice(1).join('{')}`

await page.evaluate(async (hey) => {
    window['listen'] = Function(hey)
}, { args: [ hello ] })

await page.evaluate(async () => {
    listen()
})

Both eval and Function also don't work by default using Firefox, even on the New Tab page