Bug 1113584 - Use add_task in all browser/devtools/webaudieditor browser mochitests; r=jsantell
authorPatrick Brosset <pbrosset@mozilla.com>
Fri, 19 Dec 2014 02:55:00 +0100
changeset 220861 4eb4d48d4538f0ea0beb478a9a3a178453eed543
parent 220860 f3abfa68ae44d2b61e570185ee7b0971694e4f18
child 220862 92d9d1663eb9379fc9a52dd7182162ecc13b4681
push id10527
push usercbook@mozilla.com
push dateMon, 22 Dec 2014 13:18:39 +0000
treeherderfx-team@92d9d1663eb9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjsantell
bugs1113584
milestone37.0a1
Bug 1113584 - Use add_task in all browser/devtools/webaudieditor browser mochitests; r=jsantell
browser/devtools/webaudioeditor/test/browser_audionode-actor-bypass.js
browser/devtools/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
browser/devtools/webaudioeditor/test/browser_audionode-actor-connectparam.js
browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js
browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js
browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js
browser/devtools/webaudioeditor/test/browser_wa_destroy-node-01.js
browser/devtools/webaudioeditor/test/browser_wa_first-run.js
browser/devtools/webaudioeditor/test/browser_wa_graph-click.js
browser/devtools/webaudioeditor/test/browser_wa_graph-markers.js
browser/devtools/webaudioeditor/test/browser_wa_graph-render-01.js
browser/devtools/webaudioeditor/test/browser_wa_graph-render-02.js
browser/devtools/webaudioeditor/test/browser_wa_graph-render-03.js
browser/devtools/webaudioeditor/test/browser_wa_graph-render-04.js
browser/devtools/webaudioeditor/test/browser_wa_graph-render-05.js
browser/devtools/webaudioeditor/test/browser_wa_graph-selected.js
browser/devtools/webaudioeditor/test/browser_wa_graph-zoom.js
browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js
browser/devtools/webaudioeditor/test/browser_wa_inspector.js
browser/devtools/webaudioeditor/test/browser_wa_navigate.js
browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-01.js
browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-02.js
browser/devtools/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
browser/devtools/webaudioeditor/test/browser_wa_properties-view-params-objects.js
browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js
browser/devtools/webaudioeditor/test/browser_wa_properties-view.js
browser/devtools/webaudioeditor/test/browser_wa_reset-01.js
browser/devtools/webaudioeditor/test/browser_wa_reset-02.js
browser/devtools/webaudioeditor/test/browser_wa_reset-03.js
browser/devtools/webaudioeditor/test/browser_wa_reset-04.js
browser/devtools/webaudioeditor/test/browser_webaudio-actor-connect-param.js
browser/devtools/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js
browser/devtools/webaudioeditor/test/head.js
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-bypass.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-bypass.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#bypass(), AudioNode#isBypassed()
  */
 
-function spawnTest () {
+add_task(function*() {
   let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
 
   is((yield gainNode.isBypassed()), false, "Nodes start off unbypassed.");
 
@@ -20,10 +20,9 @@ function spawnTest () {
   is((yield gainNode.isBypassed()), true, "Node is now bypassed.");
 
   info("Calling node#bypass(false)");
   yield gainNode.bypass(false);
 
   is((yield gainNode.isBypassed()), false, "Node back to being unbypassed.");
 
   yield removeTab(target.tab);
-  finish();
-}
+});
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that AudioNodeActor#connectNode() and AudioNodeActor#disconnect() work.
  * Uses the editor front as the actors do not retain connect state.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   reload(target);
 
   let [actors] = yield Promise.all([
     get3(gFront, "create-node"),
@@ -23,22 +23,18 @@ function spawnTest() {
   info("Disconnecting oscillator...");
   osc.disconnect();
   yield Promise.all([
     waitForGraphRendered(panelWin, 3, 1),
     once(gAudioNodes, "disconnect")
   ]);
   ok(true, "Oscillator disconnected, event emitted.");
 
-
   info("Reconnecting oscillator...");
   osc.connectNode(gain);
   yield Promise.all([
     waitForGraphRendered(panelWin, 3, 2),
     once(gAudioNodes, "connect")
   ]);
   ok(true, "Oscillator reconnected.");
 
-
-  yield teardown(panel);
-  finish();
-}
-
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectparam.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectparam.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that AudioNodeActor#connectParam() work.
  * Uses the editor front as the actors do not retain connect state.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   reload(target);
 
   let [actors] = yield Promise.all([
     get3(gFront, "create-node"),
@@ -24,12 +24,10 @@ function spawnTest() {
 
   osc.connectParam(gain, "gain");
   yield Promise.all([
     waitForGraphRendered(panelWin, 3, 1, 1),
     once(gAudioNodes, "connect")
   ]);
   ok(true, "Oscillator connect to Gain's Gain AudioParam, event emitted.");
 
-  yield teardown(panel);
-  finish();
-}
-
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getParamFlags()
  */
 
-function spawnTest () {
+add_task(function*() {
   let { target, front } = yield initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = yield Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let allNodeParams = yield Promise.all(nodes.map(node => node.getParams()));
   let nodeTypes = [
@@ -40,10 +40,9 @@ function spawnTest () {
         is(flags["Float32Array"], true, "`curve` param has Float32Array flag");
       } else {
         is(Object.keys(flags), 0, type + "-" + param + " has no flags set")
       }
     }
   }
 
   yield removeTab(target.tab);
-  finish();
-}
+});
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getParams()
  */
 
-function spawnTest () {
+add_task(function*() {
   let { target, front } = yield initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = yield Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let allNodeParams = yield Promise.all(nodes.map(node => node.getParams()));
   let nodeTypes = [
@@ -37,10 +37,9 @@ function spawnTest () {
         is(flags["Float32Array"], true, "`curve` param has Float32Array flag");
       } else {
         is(Object.keys(flags), 0, type + "-" + param + " has no flags set")
       }
     });
   });
 
   yield removeTab(target.tab);
-  finish();
-}
+});
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that default properties are returned with the correct type
  * from the AudioNode actors.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, front } = yield initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = yield Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let allParams = yield Promise.all(nodes.map(node => node.getParams()));
   let types = [
@@ -21,18 +21,17 @@ function spawnTest() {
     "DynamicsCompressorNode", "OscillatorNode"
   ];
 
   allParams.forEach((params, i) => {
     compare(params, NODE_DEFAULT_VALUES[types[i]], types[i]);
   });
 
   yield removeTab(target.tab);
-  finish();
-}
+});
 
 function compare (actual, expected, type) {
   actual.forEach(({ value, param }) => {
     value = getGripValue(value);
     if (typeof expected[param] === "function") {
       ok(expected[param](value), type + " has a passing value for " + param);
     }
     else {
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getParam() / AudioNode#setParam()
  */
 
-function spawnTest () {
+add_task(function*() {
   let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
 
   let freq = yield oscNode.getParam("frequency");
   info(typeof freq);
@@ -39,10 +39,9 @@ function spawnTest () {
   } catch (e) {
     ok(/is not a finite floating-point/.test(e.message), "AudioNode:setParam returns error with correct message when attempting an invalid assignment");
     is(e.type, "TypeError", "AudioNode:setParam returns error with correct type when attempting an invalid assignment");
     freq = yield oscNode.getParam("frequency");
     ise(freq, 220, "AudioNode:setParam does not modify value when an error occurs");
   }
 
   yield removeTab(target.tab);
-  finish();
-}
+});
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getType()
  */
 
-function spawnTest () {
+add_task(function*() {
   let { target, front } = yield initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = yield Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let actualTypes = yield Promise.all(nodes.map(node => node.getType()));
   let expectedTypes = [
@@ -20,10 +20,9 @@ function spawnTest () {
     "ChannelSplitterNode", "ChannelMergerNode", "DynamicsCompressorNode", "OscillatorNode"
   ];
 
   expectedTypes.forEach((type, i) => {
     is(actualTypes[i], type, type + " successfully created with correct type");
   });
 
   yield removeTab(target.tab);
-  finish();
-}
+});
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#isSource()
  */
 
-function spawnTest () {
+add_task(function*() {
   let { target, front } = yield initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = yield Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let actualTypes = yield Promise.all(nodes.map(node => node.getType()));
   let isSourceResult = yield Promise.all(nodes.map(node => node.isSource()));
@@ -19,10 +19,9 @@ function spawnTest () {
     let shouldBeSource = type === "AudioBufferSourceNode" || type === "OscillatorNode";
     if (shouldBeSource)
       is(isSourceResult[i], true, type + "'s isSource() yields into `true`");
     else
       is(isSourceResult[i], false, type + "'s isSource() yields into `false`");
   });
 
   yield removeTab(target.tab);
-  finish();
-}
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_destroy-node-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_destroy-node-01.js
@@ -4,17 +4,17 @@
 /**
  * Tests that the destruction node event is fired and that the nodes are no
  * longer stored internally in the tool, that the graph is updated properly, and
  * that selecting a soon-to-be dead node clears the inspector.
  *
  * All done in one test since this test takes a few seconds to clear GC.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(DESTROY_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, gAudioNodes } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
@@ -51,12 +51,10 @@ function spawnTest() {
 
   is(nodes, 3, "Only 3 nodes rendered in graph.");
   is(edges, 2, "Only 2 edges rendered in graph.");
 
   // Test that the inspector reset to no node selected
   ok(isVisible($("#web-audio-editor-details-pane-empty")),
     "InspectorView empty message should show if the currently selected node gets collected.");
 
-  yield teardown(panel);
-  finish();
-}
-
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_first-run.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_first-run.js
@@ -7,17 +7,17 @@
 // As part of bug 1077403, the leaking uncaught rejection should be fixed. 
 //
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Connection closed");
 
 /**
  * Tests that the reloading/onContentLoaded hooks work.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
 
   is($("#reload-notice").hidden, false,
     "The 'reload this page' notice should initially be visible.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should initially be hidden.");
   is($("#content").hidden, true,
@@ -41,11 +41,10 @@ function spawnTest() {
 
   is($("#reload-notice").hidden, true,
     "The 'reload this page' notice should be hidden after context found.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should be hidden after context found.");
   is($("#content").hidden, false,
     "The tool's content should not be hidden anymore.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-click.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-click.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the clicking on a node in the GraphView opens and sets
  * the correct node in the InspectorView
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let panelWin = panel.panelWin;
   let { gFront, $, $$, InspectorView } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
@@ -41,11 +41,10 @@ function spawnTest() {
   is(InspectorView.getCurrentAudioNode().id, nodeIds[3], "Clicking on a <rect> works as expected.");
 
   yield clickGraphNode(panelWin, $("tspan", findGraphNode(panelWin, nodeIds[4])));
   is(InspectorView.getCurrentAudioNode().id, nodeIds[4], "Clicking on a <tspan> works as expected.");
 
   ok(InspectorView.isVisible(),
     "InspectorView still visible after several nodes have been clicked.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-markers.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-markers.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the SVG marker styling is updated when devtools theme changes.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, MARKER_STYLING } = panelWin;
 
   let currentTheme = Services.prefs.getCharPref("devtools.theme");
 
   ok(MARKER_STYLING.light, "Marker styling exists for light theme.");
   ok(MARKER_STYLING.dark, "Marker styling exists for dark theme.");
@@ -42,19 +42,18 @@ function spawnTest() {
   is(getFill($("#arrowhead")), MARKER_STYLING.dark,
     "marker styling remains dark.");
 
   // Switch to back to light again
   setTheme("light");
   is(getFill($("#arrowhead")), MARKER_STYLING.light,
     "marker styling switches back to light once again.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
 
 /**
  * Returns a hex value found in styling for an element. So parses
  * <marker style="fill: #abcdef"> and returns "#abcdef"
  */
 function getFill (el) {
   return el.getAttribute("style").match(/(#.*)$/)[1];
 }
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-01.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that SVG nodes and edges were created for the Graph View.
  */
 
 let connectCount = 0;
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
@@ -32,15 +32,14 @@ function spawnTest() {
   is(findGraphEdge(panelWin, gainId, destId).toString(), "[object SVGGElement]", "found edge for gain -> dest");
 
   yield wait(1000);
 
   is(connectCount, 2, "Only two node connect events should be fired.");
 
   gAudioNodes.off("connect", onConnectNode);
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
 
 function onConnectNode () {
   ++connectCount;
 }
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-02.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests more edge rendering for complex graphs.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$ } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
@@ -40,12 +40,10 @@ function spawnTest() {
     [7, 0, "filter -> dest"],
   ];
 
   edges.forEach(([source, target, msg], i) => {
     is(findGraphEdge(panelWin, nodeIDs[source], nodeIDs[target]).toString(), "[object SVGGElement]",
       "found edge for " + msg);
   });
 
-  yield teardown(panel);
-  finish();
-}
-
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-03.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-03.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests to ensure that selected nodes stay selected on graph redraw.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   reload(target);
 
   let [actors] = yield Promise.all([
     getN(gFront, "create-node", 3),
@@ -26,12 +26,10 @@ function spawnTest() {
   // Disconnect a node to trigger a rerender
   osc.disconnect();
 
   yield once(panelWin, EVENTS.UI_GRAPH_RENDERED);
   
   ok(findGraphNode(panelWin, gain.actorID).classList.contains("selected"),
     "Node still selected after rerender.");
 
-  yield teardown(panel);
-  finish();
-}
-
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-04.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-04.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests audio param connection rendering.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(CONNECT_MULTI_PARAM_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
@@ -29,12 +29,10 @@ function spawnTest() {
     [mod2, carrier, "detune", "mod2 -> carrier[detune]"]
   ];
 
   edges.forEach(([source, target, param, msg], i) => {
     let edge = findGraphEdge(panelWin, source, target, param);
     ok(edge.classList.contains("param-connection"), "edge is classified as a param-connection");
   });
 
-  yield teardown(panel);
-  finish();
-}
-
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-05.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-05.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests to ensure that param connections trigger graph redraws
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   reload(target);
 
   let [actors] = yield Promise.all([
     getN(gFront, "create-node", 3),
@@ -20,12 +20,10 @@ function spawnTest() {
   let [dest, osc, gain] = actors;
 
   yield osc.disconnect();
 
   osc.connectParam(gain, "gain");
   yield waitForGraphRendered(panelWin, 3, 1, 1);
   ok(true, "Graph re-rendered upon param connection");
 
-  yield teardown(panel);
-  finish();
-}
-
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-selected.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-selected.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that SVG nodes and edges were created for the Graph View.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
@@ -41,12 +41,10 @@ function spawnTest() {
 
   ok(!findGraphNode(panelWin, oscId).classList.contains("selected"),
     "Previously selected node no longer has class 'selected'.");
   ok(!findGraphNode(panelWin, destId).classList.contains("selected"),
     "Non-selected nodes do not have class 'selected'.");
   ok(findGraphNode(panelWin, gainId).classList.contains("selected"),
     "Newly selected node now has class 'selected'.");
 
-  yield teardown(panel);
-  finish();
-}
-
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-zoom.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-zoom.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the graph's scale and position is reset on a page reload.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, ContextView } = panelWin;
 
   let started = once(gFront, "start-context");
 
   yield Promise.all([
     reload(target),
@@ -34,12 +34,10 @@ function spawnTest() {
     reload(target),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
 
   is(ContextView.getCurrentScale(), 1, "After refresh, graph scale is 1.");
   is(ContextView.getCurrentTranslation()[0], 20, "After refresh, x-translation is 20.");
   is(ContextView.getCurrentTranslation()[1], 20, "After refresh, y-translation is 20.");
 
-  yield teardown(panel);
-  finish();
-}
-
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the inspector toggle button shows and hides
  * the inspector panel as intended.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
@@ -56,11 +56,10 @@ function spawnTest() {
 
   ok(!isVisible($("#web-audio-editor-details-pane-empty")),
     "Empty message hides even when loading node while open.");
   ok(isVisible($("#web-audio-editor-tabs")),
     "Switches to tab view when loading node while open.");
   is($("#web-audio-inspector-title").value, "Oscillator",
     "Inspector title updates when loading node while open.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_inspector.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_inspector.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that inspector view opens on graph node click, and
  * loads the correct node inside the inspector.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
@@ -50,11 +50,10 @@ function spawnTest() {
     "default tab selected should be the parameters tab.");
 
   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
   yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
 
   is($("#web-audio-inspector-title").value, "Gain",
     "Inspector title updates when a new node is selected.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_navigate.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_navigate.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests naviating from a page to another will repopulate
  * the audio graph if both pages have an AudioContext.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $ } = panelWin;
 
   reload(target);
 
   var [actors] = yield Promise.all([
     get3(gFront, "create-node"),
@@ -35,11 +35,10 @@ function spawnTest() {
     "The 'waiting for an audio context' notice should be hidden after context found after navigation.");
   is($("#content").hidden, false,
     "The tool's content should reappear without closing and reopening the toolbox.");
 
   var { nodes, edges } = countGraphObjects(panelWin);
   ise(nodes, 14, "after navigation, should have 14 nodes");
   ise(edges, 0, "after navigation, should have 0 edges.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-01.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that properties are updated when modifying the VariablesView.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
@@ -47,21 +47,19 @@ function spawnTest() {
   yield setAndCheck(0, "type", "square", "square", "sets string as string");
 
   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
   yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
   yield setAndCheck(0, "gain", "0.005", 0.005, "sets number as number");
   yield setAndCheck(0, "gain", "0.1", 0.1, "sets float as float");
   yield setAndCheck(0, "gain", ".2", 0.2, "sets float without leading zero as float");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
 
 function setAndCheckVariable (panelWin, gVars) {
   return Task.async(function (varNum, prop, value, expected, desc) {
     yield modifyVariableView(panelWin, gVars, varNum, prop, value);
     var props = {};
     props[prop] = expected;
     checkVariableView(gVars, varNum, props, desc);
   });
 }
-
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-02.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that properties are not updated when modifying the VariablesView.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
@@ -35,11 +35,10 @@ function spawnTest() {
   } catch(e) {
     // we except modifyVariableView to fail here, because bufferSize is not writable
   }
 
   yield errorEvent;
 
   checkVariableView(gVars, 0, {bufferSize: 4096}, "check that unwritable variable is not updated");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
@@ -29,17 +29,17 @@ function waitForDeviceClosed() {
       ppmm.removeMessageListener(message, listener);
       deferred.resolve();
     }
   });
 
   return deferred.promise;
 }
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(MEDIA_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   // Auto enable getUserMedia
   let mediaPermissionPref = Services.prefs.getBoolPref(MEDIA_PERMISSION);
   Services.prefs.setBoolPref(MEDIA_PERMISSION, true);
@@ -61,14 +61,12 @@ function spawnTest() {
     click(panelWin, findGraphNode(panelWin, nodeIds[i]));
     yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
     checkVariableView(gVars, 0, NODE_DEFAULT_VALUES[types[i]], types[i]);
   }
 
   // Reset permissions on getUserMedia
   Services.prefs.setBoolPref(MEDIA_PERMISSION, mediaPermissionPref);
 
-  yield teardown(panel);
+  yield teardown(target);
 
   yield waitForDeviceClosed();
-
-  finish();
-}
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params-objects.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params-objects.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that params view correctly displays non-primitive properties
  * like AudioBuffer and Float32Array in properties of AudioNodes.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(BUFFER_AND_ARRAY_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
@@ -37,11 +37,10 @@ function spawnTest() {
   checkVariableView(gVars, 0, {
     "buffer": "AudioBuffer"
   }, "AudioBufferSourceNode's `buffer` is listed as an `AudioBuffer`.");
 
   aVar = gVars.getScopeAtIndex(0).get("buffer")
   state = aVar.target.querySelector(".theme-twisty").hasAttribute("invisible");
   ok(state, "AudioBuffer property should not have a dropdown.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that params view correctly displays all properties for nodes
  * correctly, with default values and correct types.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
@@ -29,11 +29,10 @@ function spawnTest() {
   ];
 
   for (let i = 0; i < types.length; i++) {
     click(panelWin, findGraphNode(panelWin, nodeIds[i]));
     yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
     checkVariableView(gVars, 0, NODE_DEFAULT_VALUES[types[i]], types[i]);
   }
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that params view shows params when they exist, and are hidden otherwise.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
@@ -33,11 +33,10 @@ function spawnTest() {
   click(panelWin, findGraphNode(panelWin, nodeIds[0]));
   yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
 
   ok(!isVisible($("#properties-tabpanel-content")),
     "Parameters hidden when they don't exist.");
   ok(isVisible($("#properties-tabpanel-content-empty")),
     "Empty message shown when no AudioParams exist.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-01.js
@@ -8,17 +8,17 @@
 //
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Connection closed");
 
 /**
  * Tests that reloading a tab will properly listen for the `start-context`
  * event and reshow the tools after reloading.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
 
   is($("#reload-notice").hidden, false,
     "The 'reload this page' notice should initially be visible.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should initially be hidden.");
   is($("#content").hidden, true,
@@ -56,11 +56,10 @@ function spawnTest() {
 
   is($("#reload-notice").hidden, true,
     "The 'reload this page' notice should be hidden after context found after reload.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should be hidden after context found after reload.");
   is($("#content").hidden, false,
     "The tool's content should reappear without closing and reopening the toolbox.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-02.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests reloading a tab with the tools open properly cleans up
  * the graph.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $ } = panelWin;
 
   reload(target);
 
   let [actors] = yield Promise.all([
     get3(gFront, "create-node"),
@@ -28,11 +28,10 @@ function spawnTest() {
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
 
   ({ nodes, edges } = countGraphObjects(panelWin));
   ise(nodes, 3, "after reload, should only be 3 nodes.");
   ise(edges, 2, "after reload, should only be 2 edges.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-03.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-03.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests reloading a tab with the tools open properly cleans up
  * the inspector and selected node.
  */
 
-function spawnTest() {
+add_task(function*() {
   let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, InspectorView } = panelWin;
 
   reload(target);
 
   let [actors] = yield Promise.all([
     get3(gFront, "create-node"),
@@ -39,11 +39,10 @@ function spawnTest() {
   ise(InspectorView.getCurrentAudioNode(), null,
     "InspectorView has no current node set on reset.");
 
   yield clickGraphNode(panelWin, nodeIds[2], true);
   ok(InspectorView.isVisible(),
     "InspectorView visible after selecting a node after a reset.");
   is(InspectorView.getCurrentAudioNode().id, nodeIds[2], "InspectorView has correct node set upon clicking graph node after a reset.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-04.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-04.js
@@ -7,17 +7,17 @@
 // As part of bug 1077403, the leaking uncaught rejection should be fixed. 
 //
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Connection closed");
 
 /**
  * Tests that switching to an iframe works fine.
  */
 
-function spawnTest() {
+add_task(function*() {
   Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
 
   let { target, panel, toolbox } = yield initWebAudioEditor(IFRAME_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
 
   is($("#reload-notice").hidden, false,
     "The 'reload this page' notice should initially be visible.");
   is($("#waiting-notice").hidden, true,
@@ -53,11 +53,10 @@ function spawnTest() {
 
   is($("#reload-notice").hidden, true,
     "The 'reload this page' notice should be hidden after reloading the frame.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should be hidden after reloading the frame.");
   is($("#content").hidden, false,
     "The tool's content should appear after reload.");
 
-  yield teardown(panel);
-  finish();
-}
+  yield teardown(target);
+});
--- a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-connect-param.js
+++ b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-connect-param.js
@@ -1,26 +1,25 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test the `connect-param` event on the web audio actor.
  */
 
-function spawnTest () {
+add_task(function*() {
   let { target, front } = yield initBackend(CONNECT_PARAM_URL);
   let [, , [destNode, carrierNode, modNode, gainNode], , connectParam] = yield Promise.all([
     front.setup({ reload: true }),
     once(front, "start-context"),
     getN(front, "create-node", 4),
     get2(front, "connect-node"),
     once(front, "connect-param")
   ]);
 
   info(connectParam);
 
   is(connectParam.source.actorID, modNode.actorID, "`connect-param` has correct actor for `source`");
   is(connectParam.dest.actorID, gainNode.actorID, "`connect-param` has correct actor for `dest`");
   is(connectParam.param, "gain", "`connect-param` has correct parameter name for `param`");
 
   yield removeTab(target.tab);
-  finish();
-}
+});
--- a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
+++ b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test `destroy-node` event on WebAudioActor.
  */
 
-function spawnTest () {
+add_task(function*() {
   let { target, front } = yield initBackend(DESTROY_NODES_URL);
 
   let waitUntilDestroyed = getN(front, "destroy-node", 10);
   let [, , created] = yield Promise.all([
     front.setup({ reload: true }),
     once(front, "start-context"),
     // Should create 1 destination node and 10 disposable buffer nodes
     getN(front, "create-node", 13)
@@ -24,18 +24,17 @@ function spawnTest () {
   let destroyedTypes = yield Promise.all(destroyed.map(actor => actor.getType()));
   destroyedTypes.forEach((type, i) => {
     ok(type, "AudioBufferSourceNode", "Only buffer nodes are destroyed");
     ok(actorIsInList(created, destroyed[i]),
       "`destroy-node` called only on AudioNodes in current document.");
   });
 
   yield removeTab(target.tab);
-  finish();
-}
+});
 
 function actorIsInList (list, actor) {
   for (let i = 0; i < list.length; i++) {
     if (list[i].actorID === actor.actorID)
       return list[i];
   }
   return null;
 }
--- a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js
+++ b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test basic communication of Web Audio actor
  */
 
-function spawnTest () {
+add_task(function*() {
   let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
   let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = yield Promise.all([
     front.setup({ reload: true }),
     once(front, "start-context"),
     get3(front, "create-node"),
     get2(front, "connect-node")
   ]);
 
@@ -26,10 +26,9 @@ function spawnTest () {
   is(source.actorID, oscNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on source (osc->gain)");
   is(dest.actorID, gainNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on dest (osc->gain)");
 
   ({ source, dest } = connect2);
   is(source.actorID, gainNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on source (gain->dest)");
   is(dest.actorID, destNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on dest (gain->dest)");
 
   yield removeTab(target.tab);
-  finish();
-}
+});
--- a/browser/devtools/webaudioeditor/test/head.js
+++ b/browser/devtools/webaudioeditor/test/head.js
@@ -79,21 +79,16 @@ function removeTab(aTab, aWindow) {
     info("Tab removed and finished closing.");
     deferred.resolve();
   }, false);
 
   targetBrowser.removeTab(aTab);
   return deferred.promise;
 }
 
-function handleError(aError) {
-  ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-  finish();
-}
-
 function once(aTarget, aEventName, aUseCapture = false) {
   info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
 
   let deferred = Promise.defer();
 
   for (let [add, remove] of [
     ["on", "off"], // Use event emitter before DOM events for consistency
     ["addEventListener", "removeEventListener"],
@@ -116,20 +111,20 @@ function reload(aTarget, aWaitForTargetE
   return once(aTarget, aWaitForTargetEvent);
 }
 
 function navigate(aTarget, aUrl, aWaitForTargetEvent = "navigate") {
   executeSoon(() => aTarget.activeTab.navigateTo(aUrl));
   return once(aTarget, aWaitForTargetEvent);
 }
 
-function test () {
-  Task.spawn(spawnTest).then(finish, handleError);
-}
-
+/**
+ * Adds a new tab, and instantiate a WebAudiFront object.
+ * This requires calling removeTab before the test ends.
+ */
 function initBackend(aUrl) {
   info("Initializing a web audio editor front.");
 
   if (!DebuggerServer.initialized) {
     DebuggerServer.init();
     DebuggerServer.addBrowserActors();
   }
 
@@ -139,44 +134,47 @@ function initBackend(aUrl) {
 
     yield target.makeRemote();
 
     let front = new WebAudioFront(target.client, target.form);
     return { target, front };
   });
 }
 
+/**
+ * Adds a new tab, and open the toolbox for that tab, selecting the audio editor
+ * panel.
+ * This requires calling teardown before the test ends.
+ */
 function initWebAudioEditor(aUrl) {
   info("Initializing a web audio editor pane.");
 
   return Task.spawn(function*() {
     let tab = yield addTab(aUrl);
     let target = TargetFactory.forTab(tab);
 
     yield target.makeRemote();
 
     Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
     let toolbox = yield gDevTools.showToolbox(target, "webaudioeditor");
     let panel = toolbox.getCurrentPanel();
     return { target, panel, toolbox };
   });
 }
 
-function teardown(aPanel) {
+/**
+ * Close the toolbox, destroying all panels, and remove the added test tabs.
+ */
+function teardown(aTarget) {
   info("Destroying the web audio editor.");
 
-  return Promise.all([
-    once(aPanel, "destroyed"),
-    removeTab(aPanel.target.tab)
-  ]).then(() => {
-    let gBrowser = window.gBrowser;
+  return gDevTools.closeToolbox(aTarget).then(() => {
     while (gBrowser.tabs.length > 1) {
       gBrowser.removeCurrentTab();
     }
-    gBrowser = null;
   });
 }
 
 // Due to web audio will fire most events synchronously back-to-back,
 // and we can't yield them in a chain without missing actors, this allows
 // us to listen for `n` events and return a promise resolving to them.
 //
 // Takes a `front` object that is an event emitter, the number of