How can I execute a JavaScript function within an iframe using Playwright?

I’m in the process of extracting data from a website where I occasionally encounter reCAPTCHA v2 challenges. To tackle these challenges, I utilize the 2Captcha service, and once I get the API response (a string), I need to place this response into the reCAPTCHA response field located in an iframe: textarea[name="g-recaptcha-response"]. The issue arises because the desired input field is nested within an iframe, making it challenging to use page.evaluate directly in Playwright. Below is my attempt at the solution:

let responseKey: string;

await captchaSolver
    .recaptcha({
        pageUrl: page.url(),
        captchaKey: src,
        isInvisible: true,
    })
    .then(async (result) => {
        responseKey = result.data;

        const iframeLocator = page.frameLocator('div iframe[title="reCAPTCHA"]');
        const iframe = iframeLocator.first();

        const allFrames = await page.frames();
        const reCaptchaFrame = allFrames[1];

        await reCaptchaFrame.evaluate(() => {
            const inputField = document.querySelector(
                'textarea[name="g-recaptcha-response"]'
            ) as HTMLTextAreaElement;

            inputField.value = responseKey;
        }, responseKey);

        await iframe.locator('#recaptcha-anchor > div.recaptcha-checkbox-border').click();
    })
    .catch((error) => {
        console.error(error);
    });

Unfortunately, I encountered an error stating: “Property ‘evaluate’ does not exist in type ‘FrameLocator’.ts(2339)”.

Hi Bob_Clever,

The error you're encountering is due to attempting to use evaluate on a FrameLocator. Instead, you should obtain the frame directly and interact with it. Here's a practical approach to tackle this:

let responseKey: string;

await captchaSolver
    .recaptcha({
        pageUrl: page.url(),
        captchaKey: src,
        isInvisible: true,
    })
    .then(async (result) => {
        responseKey = result.data;

        // Fetch the correct frame by its name, index, or URL
        const reCaptchaFrame = await page.waitForFrame((frame) => 
            frame.url().includes('recaptcha')
        );

        // Execute function within iframe context using evaluate
        await reCaptchaFrame.evaluate((responseKey) => {
            const inputField = document.querySelector(
                'textarea[name="g-recaptcha-response"]'
            ) as HTMLTextAreaElement;
            inputField.value = responseKey;
        }, responseKey);

        await page.locator('#recaptcha-anchor > div.recaptcha-checkbox-border').click();
    })
    .catch((error) => {
        console.error(error);
    });

This modification makes sure you're targeting the right frame using page.waitForFrame with a condition that matches the recaptcha URL. Then, it uses evaluate directly on the frame to interact with elements inside the iframe, effectively setting the value for g-recaptcha-response.

Best of luck with solving those reCAPTCHA challenges efficiently!