Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test runs only with pattern: buildapp == 'browser'
- Manifest: browser/extensions/webcompat/tests/marionette/manifest.toml
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
import json
from marionette_harness import MarionetteTestCase
class ContentScriptsAreCorrectlyPersistedTest(MarionetteTestCase):
def setUp(self):
super().setUp()
self.marionette.enforce_gecko_prefs({
"extensions.background.idle.timeout": 300000,
})
def tearDown(self):
try:
# Make sure subsequent tests get a clean profile
self.marionette.restart(in_app=False, clean=True)
finally:
super().tearDown()
def testContentScriptsAreCorrectlyPersisted(self):
# We check that as the addon starts up, the expected content scripts are all
# started up as well, and in the expected way (either being registered, or
# being already registered as they are marked as persistentAcrossSessions.
# We also check that if the list of interventions/shims changes (for instance
# in a new version of the addon), any no-longer-needed content scripts which
# are not in its list of interventions or shims are properly unregsitered,
# so they do not persist.
# First see what happens on first start-up. All content scripts ought to be
# registered by the compat addon, rather than already being there.
lastUpdateInfo, interventionContentScriptPath, shimContentScriptPath = (
self.wait_until_webcompat_addon_is_ready().values()
)
self.assertEqual(
lastUpdateInfo["alreadyRegisteredContentScripts"],
[],
"no content scripts were already registered on first startup",
)
self.assertEqual(
lastUpdateInfo["oldContentScriptsToUnregister"],
[],
"no content scripts were unregistered on first startup",
)
defaultContentScriptCount = len(lastUpdateInfo["newContentScriptsToRegister"])
self.assertTrue(
defaultContentScriptCount > 0,
"the addon registerd some content scripts on first startup",
)
self.assertTrue(
len(interventionContentScriptPath) > 0,
"sanity check: we have an intervention content script to test with",
)
self.assertTrue(
len(shimContentScriptPath) > 0,
"sanity check: we have a shim content script to test with",
)
# Restart Firefox and confirm that the content scripts were all registered
# already before the addon started up (because they are persistent)
self.marionette.restart(in_app=True)
lastUpdateInfo, _, _ = self.wait_until_webcompat_addon_is_ready().values()
self.assertEqual(
len(lastUpdateInfo["alreadyRegisteredContentScripts"]),
defaultContentScriptCount,
"all of the content scripts were already registered on second boot",
)
self.assertEqual(
lastUpdateInfo["oldContentScriptsToUnregister"],
[],
"no content scripts were unregistered on second boot",
)
self.assertEqual(
lastUpdateInfo["newContentScriptsToRegister"],
[],
"no new content scripts were registered on second boot",
)
# First we prepare a few intervention and shim configurations with content scripts
# to use for the remaining tests. (Note that we need to grab one of the content scripts
# actually in the addon right now, hence the sanity checks above).
bug1 = self.buildInterventionConfig("bug1", interventionContentScriptPath)
bug2 = self.buildInterventionConfig("bug2", interventionContentScriptPath)
bug3 = self.buildInterventionConfig("bug3", interventionContentScriptPath)
def getInterventionScriptId(bug):
return f"""webcompat intervention for {bug}: {{"js":["{interventionContentScriptPath}"]}}"""
expectedBug1ScriptId = getInterventionScriptId("bug1")
expectedBug2ScriptId = getInterventionScriptId("bug2")
expectedBug3ScriptId = getInterventionScriptId("bug3")
shim1 = self.buildShimConfig("bug1", shimContentScriptPath)
shim2 = self.buildShimConfig("bug2", shimContentScriptPath)
shim3 = self.buildShimConfig("bug3", shimContentScriptPath)
def getShimScriptId(bug):
return f"""SmartBlock shim for {bug}: {{"js":["/shims/{shimContentScriptPath}"],"matches":["*://example.com/*"],"runAt":"document_start"}}"""
expectedShim1ScriptId = getShimScriptId("bug1")
expectedShim2ScriptId = getShimScriptId("bug2")
expectedShim3ScriptId = getShimScriptId("bug3")
# Now we simulate a few addon updates to see if things work as expected.
# first across addon-restarts, and then with our RemoteSettings update client.
def runTests(whichMethod):
# Now we simulate a few addon updates to see what happens. First we simulate
# an update with no interventions or shims, to see if they're all unregistered.
lastUpdateInfo, _, _ = self.rebootWithInterventionsAndShims({}, []).values()
self.verifyBootupContentScripts(lastUpdateInfo, [], [])
# Now restart with one intervention and one shim, and confirm that both
# content scripts were newly registered.
lastUpdateInfo, _, _ = whichMethod({"bug1": bug1}, [shim1]).values()
self.verifyBootupContentScripts(
lastUpdateInfo, [expectedBug1ScriptId, expectedShim1ScriptId], []
)
# Now restart with one different intervention and shim, and
# confirm that bug1 was unregistered (since it's no longer in the new
# update's config), and that the new bug2's was registered.
lastUpdateInfo, _, _ = whichMethod({"bug2": bug2}, [shim2]).values()
self.verifyBootupContentScripts(
lastUpdateInfo,
[expectedBug2ScriptId, expectedShim2ScriptId],
[expectedBug1ScriptId, expectedShim1ScriptId],
)
# Now restart with both an already-existing intervention and shim (to see
# if it is already started before the addon does), and a new intervention
# and shim, to confirm that it's also registered.
lastUpdateInfo, _, _ = whichMethod(
{"bug2": bug2, "bug3": bug3}, [shim2, shim3]
).values()
self.verifyBootupContentScripts(
lastUpdateInfo,
[
expectedBug2ScriptId,
expectedBug3ScriptId,
expectedShim2ScriptId,
expectedShim3ScriptId,
],
[],
)
# First run the tests simulating an addon-update.
runTests(self.rebootWithInterventionsAndShims)
self.marionette.clear_pref("extensions.webcompat.test_interventions")
self.marionette.clear_pref("extensions.webcompat.test_shims")
# Now re-run the same tests with RemoteSettings client updates.
self.nextRemoteSettingsUpdateVersion = 9000
runTests(self.simulateRemoteSettingsUpdate)
def rebootWithInterventionsAndShims(self, interventions, shims):
self.marionette.set_pref(
"extensions.webcompat.test_interventions", json.dumps(interventions)
)
self.marionette.set_pref("extensions.webcompat.test_shims", json.dumps(shims))
self.marionette.restart(in_app=True)
return self.wait_until_webcompat_addon_is_ready()
def simulateRemoteSettingsUpdate(self, interventions, shims):
version = f"9999.9999.9999.{self.nextRemoteSettingsUpdateVersion}"
self.nextRemoteSettingsUpdateVersion += 1
with self.marionette.using_context("chrome"):
self.marionette.execute_async_script(
"""
const [version, interventions, shims, done] = arguments;
const { RemoteSettings } = ChromeUtils.importESModule(
);
const client = RemoteSettings("webcompat-interventions");
const update = {
id: 1, // RemoteSettings record id",
last_modified: 1368273600000,
version,
interventions,
shims,
};
client.emit("sync", { data: { current: [update] } }).then(done);
""",
script_args=(version, interventions, shims),
)
return self.wait_until_webcompat_addon_is_ready(version)
def wait_until_webcompat_addon_is_ready(self, updateVersion=None):
self.marionette.navigate("about:compat")
return self.marionette.execute_async_script(
"""
const [updateVersion, done] = arguments;
const bg = window.wrappedJSObject.browser.extension.getBackgroundPage();
const updateDone = !updateVersion ? Promise.resolve() : new Promise(updated => {
const updateCheck = setInterval(() => {
if (bg.latestUpdate == updateVersion) {
clearInterval(updateCheck);
updated();
}
}, 100);
});
Promise.allSettled([
bg.interventions.allSettled(),
bg.shims.ready(),
updateDone,
]).then(() => {
const lastUpdateInfo = {};
for (const type of ["interventions", "shims"]) {
for (const [key, value] of Object.entries(
bg[type]._lastEnabledInfo
)) {
lastUpdateInfo[key] = [
lastUpdateInfo[key] ?? [],
value.map(script => script.id),
].flat();
}
}
const interventionContentScriptPath = bg.interventions?.getAvailableInterventions()
.find(i => i.interventions.find(v => v.content_scripts.js))
?.interventions.find(v => v.content_scripts.js).content_scripts.js[0];
const shimContentScriptPath = bg.shims?.shims.values().find(s => s?.contentScripts.length)?.contentScripts[0].js[0].replace("/shims/", "");
return {
lastUpdateInfo,
interventionContentScriptPath,
shimContentScriptPath,
};
}).then(done);
""",
script_args=(updateVersion,),
)
def verifyBootupContentScripts(
self, infoOnBoot, expectAddedOrPresent, expectNotAddedOrPresent
):
registeredContentScripts = set(
infoOnBoot["alreadyRegisteredContentScripts"]
+ infoOnBoot["newContentScriptsToRegister"]
)
self.assertEqual(
len(registeredContentScripts),
len(expectAddedOrPresent),
"only expected content scripts are registered",
)
for wantedScript in expectAddedOrPresent:
self.assertTrue(
wantedScript in registeredContentScripts,
f"content script is active: {wantedScript}",
)
notRegisteredContentScripts = set(infoOnBoot["oldContentScriptsToUnregister"])
for unwantedScript in expectNotAddedOrPresent:
self.assertTrue(
(unwantedScript in notRegisteredContentScripts)
or not (unwantedScript in registeredContentScripts),
f"content script is NOT active: {unwantedScript}",
)
def buildInterventionConfig(self, id, jsContentScriptPath):
return {
"id": id,
"label": id,
"bugs": {"issue1": {"matches": ["*://example.com/*"]}},
"interventions": [
{"platforms": ["all"], "content_scripts": {"js": [jsContentScriptPath]}}
],
}
def buildShimConfig(self, id, jsContentScriptPath):
return {
"id": id,
"bug": id,
"platform": "all",
"name": id,
"matches": ["*://example.com/*"],
"contentScripts": [
{
"js": jsContentScriptPath,
"matches": ["*://example.com/*"],
"runAt": "document_start",
}
],
}