Bug 1049704 - Add a "type" parameter to Simulator configurations in WebIDE. r=jryans
authorJan Keromnes <janx@linux.com>
Wed, 04 Nov 2015 06:27:00 +0100
changeset 306848 a740558de8a6c5f08c7ec93c0130c012debf4031
parent 306847 5a3d69c1b2ad78f2b86237bdeecfe077d8da56ef
child 306849 9875f804994f1521d9d60bc3c34c0ac4fa4c667f
push id7204
push usercku@mozilla.com
push dateThu, 05 Nov 2015 15:38:26 +0000
reviewersjryans
bugs1049704
milestone45.0a1
Bug 1049704 - Add a "type" parameter to Simulator configurations in WebIDE. r=jryans
devtools/client/webide/content/simulator.js
devtools/client/webide/modules/simulators.js
devtools/client/webide/test/addons/fxos_3_0_tv_simulator-linux.xpi
devtools/client/webide/test/addons/fxos_3_0_tv_simulator-linux64.xpi
devtools/client/webide/test/addons/fxos_3_0_tv_simulator-mac64.xpi
devtools/client/webide/test/addons/fxos_3_0_tv_simulator-win32.xpi
devtools/client/webide/test/addons/simulators.json
devtools/client/webide/test/chrome.ini
devtools/client/webide/test/test_simulators.html
--- a/devtools/client/webide/content/simulator.js
+++ b/devtools/client/webide/content/simulator.js
@@ -39,17 +39,17 @@ var SimulatorEditor = {
     let form = this._form;
     if (!form) {
       // This is the first time we run `init()`, bootstrap some things.
       form = this._form = document.querySelector("#simulator-editor");
       form.addEventListener("change", this.update.bind(this));
       Simulators.on("configure", (e, simulator) => { this.edit(simulator) });
       // Extract the list of device simulation options we'll support.
       let deviceFields = form.querySelectorAll("*[data-device]");
-      this._deviceOptions = [].map.call(deviceFields, field => field.name);
+      this._deviceOptions = Array.map(deviceFields, field => field.name);
     }
 
     // Append a new <option> to a <select> (or <optgroup>) element.
     function opt(select, value, text) {
       let option = document.createElement("option");
       option.value = value;
       option.textContent = text;
       select.appendChild(option);
@@ -167,17 +167,17 @@ var SimulatorEditor = {
       simulator.addon = this._addons[value];
       simulator.options.b2gBinary = null;
     } else {
       // `value` is a custom binary path.
       simulator.options.b2gBinary = value;
       // TODO (Bug 1146531) Indicate that a custom profile is now required.
     }
     // If `form.name` contains the old version, update its last occurrence.
-    if (form.name.value.contains(oldVer) && simulator.version !== oldVer) {
+    if (form.name.value.includes(oldVer) && simulator.version !== oldVer) {
       let regex = new RegExp("(.*)" + oldVer);
       let name = form.name.value.replace(regex, "$1" + simulator.version);
       simulator.options.name = form.name.value = Simulators.uniqueName(name);
     }
   },
 
   updateVersionSelector() {
     this.updateSelector(this._form.version, this.version);
--- a/devtools/client/webide/modules/simulators.js
+++ b/devtools/client/webide/modules/simulators.js
@@ -134,18 +134,26 @@ var Simulators = {
    * Add a new simulator for `addon` if no other simulator uses it.
    */
   addIfUnusedAddon(addon, silently = false) {
     let simulators = this._simulators;
     let matching = simulators.filter(s => s.addon && s.addon.id == addon.id);
     if (matching.length > 0) {
       return promise.resolve();
     }
-    let name = addon.name.replace(" Simulator", "");
-    return this.add(new Simulator({name}, addon), silently);
+    let options = {};
+    options.name = addon.name.replace(" Simulator", "");
+    // Some addons specify a simulator type at the end of their version string,
+    // e.g. "2_5_tv".
+    let type = this.simulatorAddonVersion(addon).split("_")[2];
+    if (type) {
+      // "tv" is shorthand for type "television".
+      options.type = (type === "tv" ? "television" : type);
+    }
+    return this.add(new Simulator(options, addon), silently);
   },
 
   // TODO (Bug 1146521) Maybe find a better way to deal with removed addons?
   removeIfUsingAddon(addon) {
     let simulators = this._simulators;
     let remaining = simulators.filter(s => !s.addon || s.addon.id != addon.id);
     this._simulators = remaining;
     if (remaining.length !== simulators.length) {
@@ -195,20 +203,37 @@ var Simulators = {
     let unique = stripped;
     for (let i = 1; names[unique]; i++) {
       unique = stripped + " (" + i + ")";
     }
     return unique;
   },
 
   /**
+   * Compare an addon's ID against the expected form of a simulator addon ID,
+   * and try to extract its version if there is a match.
+   *
+   * Note: If a simulator addon is recognized, but no version can be extracted
+   * (e.g. custom RegExp pref value), we return "Unknown" to keep the returned
+   * value 'truthy'.
+   */
+  simulatorAddonVersion(addon) {
+    let match = SimulatorRegExp.exec(addon.id);
+    if (!match) {
+      return null;
+    }
+    let version = match[1];
+    return version || "Unknown";
+  },
+
+  /**
    * Detect simulator addons, including "unofficial" ones.
    */
   isSimulatorAddon(addon) {
-    return SimulatorRegExp.exec(addon.id);
+    return !!this.simulatorAddonVersion(addon);
   },
 
   emitUpdated() {
     this.emit("updated");
     this._simulators.sort(LocaleCompare);
     this._save();
   },
 
@@ -245,35 +270,45 @@ AddonManager.addAddonListener(Simulators
 EventEmitter.decorate(Simulators);
 Simulators.on("configure", Simulators.onConfigure.bind(Simulators));
 
 function Simulator(options = {}, addon = null) {
   this.addon = addon;
   this.options = options;
 
   // Fill `this.options` with default values where needed.
-  let defaults = this._defaults;
+  let defaults = this.defaults;
   for (let option in defaults) {
     if (this.options[option] == null) {
       this.options[option] = defaults[option];
     }
   }
 }
 Simulator.prototype = {
 
-  // Default simulation options, based on the Firefox OS Flame.
+  // Default simulation options.
   _defaults: {
-    width: 320,
-    height: 570,
-    pixelRatio: 1.5
+    // Based on the Firefox OS Flame.
+    phone: {
+      width: 320,
+      height: 570,
+      pixelRatio: 1.5
+    },
+    // Based on a 720p HD TV.
+    television: {
+      width: 1280,
+      height: 720,
+      pixelRatio: 1,
+    }
   },
+  _defaultType: "phone",
 
   restoreDefaults() {
+    let defaults = this.defaults;
     let options = this.options;
-    let defaults = this._defaults;
     for (let option in defaults) {
       options[option] = defaults[option];
     }
   },
 
   launch() {
     // Close already opened simulation.
     if (this.process) {
@@ -302,21 +337,30 @@ Simulator.prototype = {
     let process = this.process;
     if (!process) {
       return promise.resolve();
     }
     this.process = null;
     return process.kill();
   },
 
+  get defaults() {
+    let defaults = this._defaults;
+    return defaults[this.type] || defaults[this._defaultType];
+  },
+
   get id() {
     return this.name;
   },
 
   get name() {
     return this.options.name;
   },
 
+  get type() {
+    return this.options.type || this._defaultType;
+  },
+
   get version() {
     return this.options.b2gBinary ? "Custom" : this.addon.name.match(/\d+\.\d+/)[0];
   },
 };
 exports.Simulator = Simulator;
new file mode 100644
index 0000000000000000000000000000000000000000..f1fec6fc7bed209db842d25b93af4e9ebf03090d
GIT binary patch
literal 1087
zc$^FHW@Zs#U|`^2c#)Ls{@~-ILUASr22&OW25tr!hRnR;lEj=Gy`q$~&=5`rX62a{
zVIW*u!Og(P@`9Ox0ZgO@$L7yA5UG70e&^!$Og8pKTU|KsZdrF}N!aRZD=y7bVxE3X
z`o@WUCZ5axe%~qJvB~CP^*hV&k8hk=zrB=IKV!|6tcU(g@0eG`hzHr~|C#1h_B4L^
zy+5+a8)Ce<`*)dI9ukjLxRVv!d^l(J-$Qd6OfE<@UbrGwa+CG@uh6im4ee&DQ{FK$
zG+WPHyy*vH98(9A+=1;vEKhdb>|NDveS?GBOIYO6q=}K<5eM8Z-4a~#mV4@^qR_fm
zVnqub(p8rH)?jhmpq7=E81eltbM8FnaMO~%hZrQhZS(&<ekOM3*D1X}w$+<&vdrG0
z>_16`vr@!O(Af3l8^4J+j8gUg#2Fv{<x+M><57)N(qFYbAA-$a|6$9`OL0DzDljwe
z<jm|VIr_6aQ$0gOSGP`5je6zc*Y?#%EOY18GST%vy5hpZuH~wF%=UNZeV*tq<NBN1
zcX{D-sY8bTyV)+UI9+<&aAKzKxx9-@Qlj2)uxF%fT_yPQJ#(sjwe7>VYwvh9FTGdh
z*6-U|>Gtc6$vZ_3sppoZ=?9KI{hv^GG&wGAlg*<hUwc3AX(=){Rm3)W80_p>zpUP0
zM&ioW5dYt2)jbbbpA#%QC3>5&zv9Eb=W-hoFMY_la_`9Jt9^S6q6BL~S??%!N;^I}
zBBq+<!@hr?y^~Bqgx$f%1s7wR`%JvL7AvyM$$s0p?MMBSjeYMNcH8V}OFm+KyIgKp
zyxP})i~;bpwYf6cZAs9)1y6vyMn(n(9$?x^%Fiz;E-6YZ(90?YC$32czlNw`Ca&6k
zU+*IZ0&VaAioOp#ed&apxD99125#=Ej~2c`)|1k_jBfsF{C~HE)pqCfpEb{FB`-fZ
za#^ps_1SIRPn+6`U*1pa@>}ViEp_=(=93<G&68DkIzR82ZnE%P(aZR~Ynhm2ZLciX
zbZ#pyp1{x7SfEpzaC^bgd29P`<`=x$@HWrNhDRY{=KcEozG|H*g02rOvLEeRQ63R-
zoU0%%m5nd-aU_?HtH=5yrmp-!J*z&SoAL7a?#W-0RxMkZw=8;oW}DF3O)dpzycA<B
zgKjBQo)TLB@#k0fE47!5;+2jq@|#w{sQEE|aeja|Ba<96u6!uL00O{ZW7yINVxi<p
pR!FWy3p8W{F#{jjz!;!4s8PWRi3$u8S=m6kn1FCGkZxlJ@c_;E!JPmA
new file mode 100644
index 0000000000000000000000000000000000000000..0fd70ca9f13a12362ab54df2691c8aeee8b6d0ab
GIT binary patch
literal 1087
zc$^FHW@Zs#U|`^2*j$<Hwj^lYf+s*;BO?O?4}%OtQht6(aY<2PfnHW|Xb2|*^Q41c
zLqNE+f}4Sn<pnbX1DL4o_w_zvAkg;yuju>0)0a-jiQ8~CZQ$mv`e@-BWIZX(%jo8>
z#{YLqSZ#Ms|5@{_R`T+rBbW86Tc6$5{j{mA_~reyF29xT*;1DuWj^U~*F0Hur}OiU
z=_U)$6}^n#yOxPb*7nMBP3N}a;tBkGjRiWj3AYy<owv6CW`4n|4R7<TY<LtRX5O#Q
z@2l3CBIx?iBKy(473C2T$GHmPQrY-IA4hWOxO%KVV(Q8t)U)dIxfw5y@1Fc6Y1Oio
zdCQ{bXSNBg-Q-em#!E5AGU%2<<td@{AAf#zzfyb2C|>E<BEM-BjG7<g7v~4SLyDy+
z+5N%CM}^`{3=F0$3=G`Bkjl&}E=kPE(JM+x0|(TM%@$!Gj22L-!Lj+X4Mb|6hu^ul
zJ(G=n(N-7EyIa;>S`xPU+KNl_l$fU<lfH3cpNZ%4zu$KXcx<wHSpClO`{NsD)^9Im
z)z4USCF`L-(>vx>G2%hC`hTW*l|7ALe(#TL@`f01?*3h-mWRY+74BpOHy_TK{rAwE
z29pa?jTf$nmE2_g{wp+WYD2r(>Xdhk49(Uv7jOE(7{}DXBzIuD5X+NYH+xsLTi@W|
z_7WDkG-+a_cf<j=OSc4<yyc#{sVKDWl~~b2hjf)CzcpAKH>hQ$B}RPz%bYvUIo!15
z?;!>WZ`=HTkDrO%`E^R~k8Snln=G?;DEm)R;j9#K6Et=``NnVJ4Wm^3KXJx~f4P+1
z(RfrNmGoC_&xc_1*MHb@^HQA8r3%c<J2^A^N{;?4&s5J4(bcV!RHI(G__clY5zE|p
zwM=yVkFL0|uxq)h9<%-3d7mfx%eemL_FY~$UFwjb|8BO+D^8alH=LO1doJ(dl9Z@7
z9PAk>TUQDGe9xRJUv2yF?b<tD%}ei<x%K<DR=WMVWAaXsL+ZI@Y5IX<PyZ*>9Ziml
z+hp^o$=BY`ds>RjO%<_?9tJym)-S8~myx)#HN^k-S#{3?*5?GvPKn-T?63H+@44KD
z#7iG?uG~BF`D))DgDAn8P}V!jozjkvj)<vd`LOTbXYV9a5Mg)lalysd<~|dzuEmNh
zbF$xdZu?RHWMkhuhut=N+LDi0-!7Ni6|eU7A7g+wBa<96u6!uL00KZJ!<I%63pH1=
pLUJWqLPItYGrc04s0E}@QZFkc^<o&v$_CQJ1cZx$bQ?2>2LLYx!oC0i
new file mode 100644
index 0000000000000000000000000000000000000000..434ed5e0db8eda2e865237330d91b3648cda3049
GIT binary patch
literal 1087
zc$^FHW@Zs#U|`^2&`nNufAH~9p*Rx*gDDFG12=;VLuOuaNn%cpUQtR~Xb2|*^U|3X
zVJm^Sw1S&~k>v$50|S^y4UWy9Z6H$nJp9hZ?U`)si?+IO-rch9(vq;%*H&Dbr^Gz{
znDmVk`%FBS|NXvGz+;om!|HdI-yh#NvwnLitA56sD_IZyncgw4iV+X8)&DcitL$m~
z@_T<|lQ+b8bNBBuwLByqt8gbPxcP9-?7xTRG?-kFYP@hotmG!^_g|r5QybdNR;RpU
zWN5aYxp>nL#yF-9Cb<LKg;<{Ky4ky`-TDRxx0kTUrAZSby(12|UAiT><SqBqO+}$~
zuf&QLI;5*C`K`g?xIrx|EivNzU*_C-&f%sde-AN8c-!Xxd;Co7&aYE?e{8Ea-(;D+
zL)m|l3TLH=o1n4l$v1uzZy2TO|A{j`{L7{6j>e-JsiePZdp-o4zy8CPo0sBzE>&P=
z-pQHSS90`cd8T@Xh^}s(q#E_g#jowFk67l;t7W3=e{{u#g<Z>4^_cDN&ig#kU&i$}
zx9{@8=~9Ob{dconUU9ngxZ%W1-*b5vm!w3!;b6~5*}6*b=X>T<`D)vTZ`a=OYF>J;
z%&p(IwbJd^9g}y898%9MOVbY=d-^}2?r3sc+$NhxO}_Si-qTWKZmNiF^f1`jvwm5<
zzl_9{ts(xu&#HSKus$bPc1rX%V}Hemeb41KBwqTEbLHNV&sY2Q7(@xygtFdI?v!?X
zbVN)w%ZGjcK6@vbf(W~Vj|(owHusr$buCt8nUnptbK8&lCmZ|TIqbIC)0TY1`gXb8
zu6VVt{}=<{X=`(3vfGlNc?+HZd5w$=3_QTJm6V@fQe0A$SfH0x3{G5=4t@;*VYI|m
z+wbdr#6Y0!{a?}dfu}E>kQ2AzY}&xhUG>qzH^_QYnwQbdUyc9omay9Hoc^=sS*_&d
zM@KH}RkuF7t@~+HTk*^LX<dFR-Ls`GKgxX4<F0wK>Q3k99n(z~o-2A8zjrMYldSEP
z<(kfI#l;i&`5Fs!Y7=fRI67}_|IPe@R~z2uS=sO?M9jQjpWj!lGeyw#p+)wieJjc%
zB93zv#HF(Fg+7kt(sA`zf5g<4Kd5Kb=W{b&9^XCrOVX-kEAy5`&(CZVTD!@m;Eb1I
zjAhU*h00Sx>p%Yd>VBp6l2N?Uu|<B<Di}3C#xKqf@MdI^W5$&aB^W>e7;Fq%8bK_S
rT*(T_m1u#6Y#?UfBO4e4v<5XQSRqk?VInIVNEZ_jE(X$V%pe{B<B7up
new file mode 100644
index 0000000000000000000000000000000000000000..aea5862b0a016a5874ba3af1235b146417a75416
GIT binary patch
literal 1087
zc$^FHW@Zs#U|`^22ue<NfAH~9p*Rx*gDDFG12=;VLuOuaNn%cpUQtR~Xb2|*^Xr)w
zVIW*u!Og(P@`9Ox0ZgO@$L7yA5UG70e&^!$Og8pKTU|KsZdrF}N!aRZD=y7bVxE3X
z`o@WUCZ5axe%~qJvB~CP^*hV&k8hk=zrB=IKV!|6tcU(g@0eG`hzHr~|C#1h_B4L^
zy+5+a8)Ce<`*)dI9ukjLxRVv!d^l(J-$Qd6OfE<@UbrGwa+CG@uh6im4ee&DQ{FK$
zG+WPHyy*vH98(9A+=1;vEKhdb>|NDveS?GBOIYO6q=}K<5eM8Z-4a~#mV4@^qR_fm
zVnqub(p8rH)?jhmpq7=E81eltbM8FnaMO~%hZrQhZS(&<ekOM3*D1X}w$+<&vdrG0
z>_16`vr@!O(Af3l8^4J+j8gUg#2Fv{<x+M><57)N(qFYbAA-$a|6$9`OL0DzDljwe
z<jm|VIr_6aQ$0gOSGP`5je6zc*Y?#%EOY18GST%vy5hpZuH~wF%=UNZeV*tq<NBN1
zcX{D-sY8bTyV)+UI9+<&aAKzKxx9-@Qlj2)uxF%fT_yPQJ#(sjwe7>VYwvh9FTGdh
z*6-U|>Gtc6$vZ_3sppoZ=?9KI{hv^GG&wGAlg*<hUwc3AX(=){Rm3)W80_p>zpUP0
zM&ioW5dYt2)jbbbpA#%QC3>5&zv9Eb=W-hoFMY_la_`9Jt9^S6q6BL~S??%!N;^I}
zBBq+<!@hr?y^~Bqgx$f%1s7wR`%JvL7AvyM$$s0p?MMBSjeYMNcH8V}OFm+KyIgKp
zyxP})i~;bpwYf6cZAs9)1y6vyMn(n(9$?x^%Fiz;E-6YZ(90?YC$32czlMM?TH>nh
z_w_zvAkg;yuju>0)0a-jiQ8~CZQ$mv`e@-BWIZX(%jo8>#{YLqSZ#Ms|5@{_R`T+r
zBbW86Tc6$5{j{mA_~reyF29xT*;1DuWj^U~*F0Hur}OiU=_U)$6}^n#yOxPb*7nMB
zP3N}a;tBkGjRiWj3AYy<owv6CW`4n|4R7<TY<LtRX5O#Q@2l3CBIx?iBKy(473C2T
z$GHmPQrY-IA4hWOxO%KVV(Q8t)U)dIxfw5y@1Fc6Y1OiodCQ{bXSNBg-Q-em#!E5A
zGU%2<<td@{AAf#zzfyb2C|>E<BEM-BjG7<g7v~3fGcw6B<I0B;3?KjuHij*YAQnol
qWQF8Pv_L~P5Hs+R4U7R=gBlgAkf^{gk(CXkiwOu91L-zq5Dx(20>l~s
--- a/devtools/client/webide/test/addons/simulators.json
+++ b/devtools/client/webide/test/addons/simulators.json
@@ -1,4 +1,4 @@
 {
   "stable": ["1.0", "2.0"],
-  "unstable": ["3.0"]
+  "unstable": ["3.0", "3.0_tv"]
 }
--- a/devtools/client/webide/test/chrome.ini
+++ b/devtools/client/webide/test/chrome.ini
@@ -13,16 +13,20 @@ support-files =
   addons/fxos_2_0_simulator-linux.xpi
   addons/fxos_2_0_simulator-linux64.xpi
   addons/fxos_2_0_simulator-win32.xpi
   addons/fxos_2_0_simulator-mac64.xpi
   addons/fxos_3_0_simulator-linux.xpi
   addons/fxos_3_0_simulator-linux64.xpi
   addons/fxos_3_0_simulator-win32.xpi
   addons/fxos_3_0_simulator-mac64.xpi
+  addons/fxos_3_0_tv_simulator-linux.xpi
+  addons/fxos_3_0_tv_simulator-linux64.xpi
+  addons/fxos_3_0_tv_simulator-win32.xpi
+  addons/fxos_3_0_tv_simulator-mac64.xpi
   addons/adbhelper-linux.xpi
   addons/adbhelper-linux64.xpi
   addons/adbhelper-win32.xpi
   addons/adbhelper-mac64.xpi
   addons/fxdt-adapters-linux32.xpi
   addons/fxdt-adapters-linux64.xpi
   addons/fxdt-adapters-win32.xpi
   addons/fxdt-adapters-mac64.xpi
--- a/devtools/client/webide/test/test_simulators.html
+++ b/devtools/client/webide/test/test_simulators.html
@@ -96,23 +96,23 @@
           yield addonStatus(sim20, "installed");
 
           is(findAll(".runtime-panel-item-simulator").length, 2, "Two simulators in runtime panel");
 
           // Dry run a simulator to verify that its parameters look right.
 
           let params = yield runSimulator(0);
 
-          ok(params.path.contains(sim10.addonID) && params.path.contains("b2g-bin"), "Simulator binary path looks right");
+          ok(params.path.includes(sim10.addonID) && params.path.includes("b2g-bin"), "Simulator binary path looks right");
 
           let pid = params.args.indexOf("-profile");
           ok(pid > -1, "Simulator process arguments have --profile");
 
           let profilePath = params.args[pid + 1];
-          ok(profilePath.contains(sim10.addonID) && profilePath.contains("profile"), "Simulator profile path looks right");
+          ok(profilePath.includes(sim10.addonID) && profilePath.includes("profile"), "Simulator profile path looks right");
 
           ok(params.args.indexOf("-dbgport") > -1 || params.args.indexOf("-start-debugger-server") > -1, "Simulator process arguments have a debugger port");
 
           ok(params.args.indexOf("-no-remote") > -1, "Simulator process arguments have --no-remote");
 
           yield nextTick();
 
           // Configure the fake 1.0 simulator.
@@ -229,31 +229,31 @@
           is(params.args[pid + 1], fakeProfile.path, "Simulator process still uses custom profile directory");
 
           yield set(form.version, "custom");
 
           // Test `device`.
 
           let defaults = Simulator.prototype._defaults;
 
-          for (let param in defaults) {
-            is(form[param].value, String(defaults[param]), "Default value for device " + param);
+          for (let param in defaults.phone) {
+            is(form[param].value, String(defaults.phone[param]), "Default phone value for device " + param);
           }
 
           let width = 5000, height = 4000;
           yield set(form.width, width);
           yield set(form.height, height);
 
           is(form.device.value, "custom", "Device selector is custom");
 
           params = yield runSimulator(0);
 
           let sid = params.args.indexOf("-screen");
           ok(sid > -1, "Simulator process arguments have --screen");
-          ok(params.args[sid + 1].contains(width + "x" + height), "Simulator screen resolution looks right");
+          ok(params.args[sid + 1].includes(width + "x" + height), "Simulator screen resolution looks right");
 
           yield set(form.version, sim10.addonID);
 
           // Configure the fake 2.0 simulator.
 
           simulatorList.querySelectorAll(".configure-button")[1].click();
           yield nextTick();
 
@@ -272,54 +272,77 @@
           is(form.name.value, findAll(".runtime-panel-item-simulator")[1].textContent, "Deduplicated simulator name stayed consistent");
 
           yield set(form.version, sim20.addonID);
 
           is(form.name.value, customName + "2.0", "Name deduplication was undone when possible");
 
           // Test `device`.
 
-          for (let param in defaults) {
-            is(form[param].value, String(defaults[param]), "Default value for device " + param);
+          for (let param in defaults.phone) {
+            is(form[param].value, String(defaults.phone[param]), "Default phone value for device " + param);
           }
 
           let devices = yield GetDevices();
           devices = devices[devices.TYPES[0]];
           let device = devices[devices.length - 1];
 
           yield set(form.device, device.name);
 
           is(form.device.value, device.name, "Device selector was changed");
           is(form.width.value, String(device.width), "New device width is correct");
           is(form.height.value, String(device.height), "New device height is correct");
 
           params = yield runSimulator(1);
 
           sid = params.args.indexOf("-screen");
-          ok(params.args[sid + 1].contains(device.width + "x" + device.height), "Simulator screen resolution looks right");
+          ok(params.args[sid + 1].includes(device.width + "x" + device.height), "Simulator screen resolution looks right");
 
           // Restore default simulator options.
 
           doc.querySelector("#reset").click();
           yield nextTick();
 
-          for (let param in defaults) {
-            is(form[param].value, String(defaults[param]), "Default value for device " + param);
+          for (let param in defaults.phone) {
+            is(form[param].value, String(defaults.phone[param]), "Default phone value for device " + param);
+          }
+
+          // Install and configure the fake "Firefox OS 3.0 TV" simulator addon.
+
+          let sim30tv = addons.simulators.filter(a => a.version == "3.0_tv")[0];
+
+          sim30tv.install();
+
+          yield addonStatus(sim30tv, "installed");
+
+          is(findAll(".runtime-panel-item-simulator").length, 3, "Three simulators in runtime panel");
+
+          simulatorList.querySelectorAll(".configure-button")[2].click();
+          yield nextTick();
+
+          for (let param in defaults.television) {
+            is(form[param].value, String(defaults.television[param]), "Default TV value for device " + param);
           }
 
           // Force reload the list of simulators.
 
           Simulators._loadingPromise = null;
           Simulators._simulators = [];
           yield Simulators._load();
           yield nextTick();
 
-          is(findAll(".runtime-panel-item-simulator").length, 2, "Two simulators saved and reloaded " + Simulators._simulators.map(s => s.name).join(','));
+          is(findAll(".runtime-panel-item-simulator").length, 3, "Three simulators saved and reloaded " + Simulators._simulators.map(s => s.name).join(','));
+
+          // Uninstall the 3.0 TV and 2.0 addons, and watch their Simulator objects disappear.
 
-          // Uninstall the 2.0 addon and watch its Simulator object disappear.
+          sim30tv.uninstall();
+
+          yield addonStatus(sim30tv, "uninstalled");
+
+          is(findAll(".runtime-panel-item-simulator").length, 2, "Two simulators left in runtime panel");
 
           sim20.uninstall();
 
           yield addonStatus(sim20, "uninstalled");
 
           is(findAll(".runtime-panel-item-simulator").length, 1, "One simulator left in runtime panel");
 
           // Remove 1.0 simulator.