2

My extension architecture:

  1. Background page stores the extension state. I think this is good as keeping the state in one place makes debugging easier.
  2. Content scripts do some things on the webpage based on the extension state.
  3. Every time user makes an update to the extension state through the settings page, they are updated in the background page which then sends a message to the content scripts to update their data accordingly.

Through Jest+Puppeteer, I intend to simulate different changes in state and test them. To that end, here's my MCVE background page:

chrome.runtime.onInstalled.addListener(function (details) {
    if (details.reason === "install") {
        console.log("installed at " + new Date());
    }
});

window.sendMessage = function () {
    chrome.tabs.query({}, function (tabs) {
        if (chrome.runtime.lastError) {
            console.log(chrome.runtime.lastError.message);
        }

        console.log(tabs);
        if (!tabs) return;

        for (const tab of tabs) {
            if (tab.url && /^https:\/\//.test(tab.url)) {
                chrome.tabs.sendMessage(tab.id, { "message": "hey" });
            }
        }

    });
};
console.log("defined fn " + window.sendMessage)

and the following test.js

/**
 * Wait for given milliseconds
 * @param {Number} milliseconds to wait
 */
function sleep(milliseconds) {
    return new Promise((resolve) => {
        setTimeout(() => resolve(), milliseconds);
    });
}

// some random webpages
const testURLs = [
    {
        url:
            "https://stackoverflow.com/questions/50990292/"
            + "using-octal-character-gives-warning-multi-character-character-constant",
    },
    {
        url:
            "https://serverfault.com/questions/971011/"
            + "how-to-check-if-an-active-directory-server-is-reachable-from-an-ubuntu-apache-ph",
    },
];

const usablePages = [],
    JEST_TIMEOUT = 60000;

/*
 * add this since jest's default timeout is 5s only; Loading a page might take longer
 */
jest.setTimeout(JEST_TIMEOUT);

async function getBackgroundPage() {
    const targets = await browser.targets(),
        backgroundPageTarget = targets.find(
            target => target.type() === "background_page",
        ),
        backgroundPage = await backgroundPageTarget.page();

    return backgroundPage;
}

async function sendMsgs() {
    const bgPage = await getBackgroundPage();
    await bgPage.evaluateHandle(`window.sendMessage();`);
}

beforeAll(async () => {
    for (const testURL of testURLs) {
        const usablePage = await browser.newPage();
        await usablePage.setViewport({ width: 1920, height: 1080 });
        await usablePage.goto(testURL.url);
    }
    // for debugging, so you can open console and check in 15secs
    await sleep(15000);
    sendMsgs();
    await sleep(15000);
});

it("simple test", function () {
    expect(2).toBe(2);
});

This code gives me the following runtime.lastError:

Access to extension API denied.

I have declared the ["tabs", "<all_urls>"] permission in my manifest.json already. What else can I do to resolve this issue?

My jest puppeteer config:

const pathToExtension = require("path").join(__dirname, "dist");

module.exports = {
    launch: {
        headless: false,
        args: [
            `--disable-extensions-except=${pathToExtension}`,
            `--load-extension=${pathToExtension}`,
            "--single-process",
        ],
        sloMo: 250,
    },
};
Gaurang Tandon
  • 6,504
  • 11
  • 47
  • 84

1 Answers1

0

I've had the same problem for some time now, and upon removing

  '--single-process'

from the puppeteer args it looks like it worked.