Bug 1331705 - shield-recipe-client: Do not use an XRay-ed Promise during recipe execution, r=Gijs
MozReview-Commit-ID: DEM6lUiCHnj
--- a/browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
+++ b/browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
@@ -123,40 +123,57 @@ this.RecipeRunner = {
},
/**
* Execute a recipe by fetching it action and executing it.
* @param {Object} recipe A recipe to execute
* @promise Resolves when the action has executed
*/
executeRecipe: Task.async(function* (recipe, extraContext) {
- const sandboxManager = new SandboxManager();
- const {sandbox} = sandboxManager;
-
const action = yield NormandyApi.fetchAction(recipe.action);
const response = yield fetch(action.implementation_url);
const actionScript = yield response.text();
- const prepScript = `
- var pendingAction = null;
+ yield this.executeAction(recipe, extraContext, actionScript);
+ }),
- function registerAction(name, Action) {
- let a = new Action(sandboxedDriver, sandboxedRecipe);
- pendingAction = a.execute()
- .catch(err => sandboxedDriver.log(err, 'error'));
+ /**
+ * Execute an action in a sandbox for a specific recipe.
+ * @param {Object} recipe A recipe to execute
+ * @param {Object} extraContext Extra data about the user, see NormandyDriver
+ * @param {String} actionScript The JavaScript for the action to execute.
+ * @promise Resolves or rejects when the action has executed or failed.
+ */
+ executeAction(recipe, extraContext, actionScript) {
+ return new Promise((resolve, reject) => {
+ const sandboxManager = new SandboxManager();
+ const {sandbox} = sandboxManager;
+ const prepScript = `
+ function registerAction(name, Action) {
+ let a = new Action(sandboxedDriver, sandboxedRecipe);
+ a.execute()
+ .then(actionFinished)
+ .catch(err => sandboxedDriver.log(err, 'error'));
+ };
+
+ window.registerAction = registerAction;
+ window.setTimeout = sandboxedDriver.setTimeout;
+ window.clearTimeout = sandboxedDriver.clearTimeout;
+ `;
+
+ const driver = new NormandyDriver(sandboxManager, extraContext);
+ sandbox.sandboxedDriver = Cu.cloneInto(driver, sandbox, {cloneFunctions: true});
+ sandbox.sandboxedRecipe = Cu.cloneInto(recipe, sandbox);
+ sandbox.actionFinished = result => {
+ sandboxManager.removeHold("recipeExecution");
+ resolve(result);
+ };
+ sandbox.actionFailed = err => {
+ sandboxManager.removeHold("recipeExecution");
+ reject(err);
};
- window.registerAction = registerAction;
- window.setTimeout = sandboxedDriver.setTimeout;
- window.clearTimeout = sandboxedDriver.clearTimeout;
- `;
-
- const driver = new NormandyDriver(sandboxManager, extraContext);
- sandbox.sandboxedDriver = Cu.cloneInto(driver, sandbox, {cloneFunctions: true});
- sandbox.sandboxedRecipe = Cu.cloneInto(recipe, sandbox);
-
- Cu.evalInSandbox(prepScript, sandbox);
- Cu.evalInSandbox(actionScript, sandbox);
-
- sandboxManager.addHold("recipeExecution");
- sandbox.pendingAction.then(() => sandboxManager.removeHold("recipeExecution"));
- }),
+ sandboxManager.addHold("recipeExecution");
+ Cu.evalInSandbox(prepScript, sandbox);
+ Cu.evalInSandbox(actionScript, sandbox);
+ });
+ },
};
--- a/browser/extensions/shield-recipe-client/test/browser.ini
+++ b/browser/extensions/shield-recipe-client/test/browser.ini
@@ -1,8 +1,9 @@
[browser_driver_uuids.js]
[browser_env_expressions.js]
[browser_EventEmitter.js]
[browser_Storage.js]
[browser_Heartbeat.js]
[browser_NormandyApi.js]
support-files =
test_server.sjs
+[browser_RecipeRunner.js]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/shield-recipe-client/test/browser_RecipeRunner.js
@@ -0,0 +1,29 @@
+"use strict";
+
+const {utils: Cu} = Components;
+Cu.import("resource://shield-recipe-client/lib/RecipeRunner.jsm", this);
+
+add_task(function*() {
+ // Test that RecipeRunner can execute a basic recipe/action.
+ const recipe = {
+ foo: "bar",
+ };
+ const actionScript = `
+ class TestAction {
+ constructor(driver, recipe) {
+ this.recipe = recipe;
+ }
+
+ execute() {
+ return new Promise(resolve => {
+ resolve(this.recipe.foo);
+ });
+ }
+ }
+
+ registerAction('test-action', TestAction);
+ `;
+
+ const result = yield RecipeRunner.executeAction(recipe, {}, actionScript);
+ is(result, "bar", "Recipe executed correctly");
+});