My extension architecture:
- Background page stores the extension state. I think this is good as keeping the state in one place makes debugging easier.
- Content scripts do some things on the webpage based on the extension state.
- 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,
},
};