test-fennec-tabs.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. 'use strict';
  5. const { Cc, Ci } = require('chrome');
  6. const { Loader, LoaderWithHookedConsole } = require('sdk/test/loader');
  7. const timer = require('sdk/timers');
  8. const tabs = require('sdk/tabs');
  9. const windows = require('sdk/windows');
  10. const { set: setPref } = require("sdk/preferences/service");
  11. const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
  12. const tabsLen = tabs.length;
  13. const URL = 'data:text/html;charset=utf-8,<html><head><title>#title#</title></head></html>';
  14. // Fennec error message dispatched on all currently unimplement tab features,
  15. // that match LoaderWithHookedConsole messages object pattern
  16. const ERR_FENNEC_MSG = {
  17. type: "error",
  18. msg: "This method is not yet supported by Fennec"
  19. };
  20. // TEST: tab unloader
  21. exports.testAutomaticDestroy = function(assert, done) {
  22. let called = false;
  23. let loader2 = Loader(module);
  24. let loader3 = Loader(module);
  25. let tabs2 = loader2.require('sdk/tabs');
  26. let tabs3 = loader3.require('sdk/tabs');
  27. let tabs2Len = tabs2.length;
  28. tabs2.on('open', function onOpen(tab) {
  29. assert.fail("an onOpen listener was called that should not have been");
  30. called = true;
  31. });
  32. tabs2.on('ready', function onReady(tab) {
  33. assert.fail("an onReady listener was called that should not have been");
  34. called = true;
  35. });
  36. tabs2.on('select', function onSelect(tab) {
  37. assert.fail("an onSelect listener was called that should not have been");
  38. called = true;
  39. });
  40. tabs2.on('close', function onClose(tab) {
  41. assert.fail("an onClose listener was called that should not have been");
  42. called = true;
  43. });
  44. loader2.unload();
  45. tabs3.on('open', function onOpen(tab) {
  46. assert.pass("an onOpen listener was called for tabs3");
  47. tab.on('ready', function onReady(tab) {
  48. assert.fail("an onReady listener was called that should not have been");
  49. called = true;
  50. });
  51. tab.on('select', function onSelect(tab) {
  52. assert.fail("an onSelect listener was called that should not have been");
  53. called = true;
  54. });
  55. tab.on('close', function onClose(tab) {
  56. assert.fail("an onClose listener was called that should not have been");
  57. called = true;
  58. });
  59. });
  60. tabs3.open(URL.replace(/#title#/, 'tabs3'));
  61. loader3.unload();
  62. // Fire a tab event and ensure that the destroyed tab is inactive
  63. tabs.once('open', function(tab) {
  64. assert.pass('tabs.once("open") works!');
  65. assert.equal(tabs2Len, tabs2.length, "tabs2 length was not changed");
  66. assert.equal(tabs.length, (tabs2.length+2), "tabs.length > tabs2.length");
  67. tab.once('ready', function() {
  68. assert.pass('tab.once("ready") works!');
  69. tab.once('close', function() {
  70. assert.pass('tab.once("close") works!');
  71. timer.setTimeout(function () {
  72. assert.ok(!called, "Unloaded tab module is destroyed and inactive");
  73. // end test
  74. done();
  75. });
  76. });
  77. tab.close();
  78. });
  79. });
  80. tabs.open('data:text/html;charset=utf-8,foo');
  81. };
  82. // TEST: tab properties
  83. exports.testTabProperties = function(assert, done) {
  84. setPref(DEPRECATE_PREF, true);
  85. let { loader, messages } = LoaderWithHookedConsole();
  86. let tabs = loader.require('sdk/tabs');
  87. let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>";
  88. let tabsLen = tabs.length;
  89. tabs.open({
  90. url: url,
  91. onReady: function(tab) {
  92. assert.equal(tab.title, "foo", "title of the new tab matches");
  93. assert.equal(tab.url, url, "URL of the new tab matches");
  94. assert.ok(tab.favicon, "favicon of the new tab is not empty");
  95. // TODO: remove need for this test by implementing the favicon feature
  96. assert.equal(messages[0].msg,
  97. "tab.favicon is deprecated, and " +
  98. "currently favicon helpers are not yet supported " +
  99. "by Fennec",
  100. "favicon logs an error for now");
  101. assert.equal(tab.style, null, "style of the new tab matches");
  102. assert.equal(tab.index, tabsLen, "index of the new tab matches");
  103. assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
  104. assert.notEqual(tab.id, null, "a tab object always has an id property");
  105. tab.close(function() {
  106. loader.unload();
  107. // end test
  108. done();
  109. });
  110. }
  111. });
  112. };
  113. // TEST: tabs iterator and length property
  114. exports.testTabsIteratorAndLength = function(assert, done) {
  115. let newTabs = [];
  116. let startCount = 0;
  117. for each (let t in tabs) startCount++;
  118. assert.equal(startCount, tabs.length, "length property is correct");
  119. let url = "data:text/html;charset=utf-8,testTabsIteratorAndLength";
  120. tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)});
  121. tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)});
  122. tabs.open({
  123. url: url,
  124. onOpen: function(tab) {
  125. let count = 0;
  126. for each (let t in tabs) count++;
  127. assert.equal(count, startCount + 3, "iterated tab count matches");
  128. assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property");
  129. let newTabsLength = newTabs.length;
  130. newTabs.forEach(function(t) t.close(function() {
  131. if (--newTabsLength > 0) return;
  132. tab.close(done);
  133. }));
  134. }
  135. });
  136. };
  137. // TEST: tab.url setter
  138. exports.testTabLocation = function(assert, done) {
  139. let url1 = "data:text/html;charset=utf-8,foo";
  140. let url2 = "data:text/html;charset=utf-8,bar";
  141. tabs.on('ready', function onReady(tab) {
  142. if (tab.url != url2)
  143. return;
  144. tabs.removeListener('ready', onReady);
  145. assert.pass("tab loaded the correct url");
  146. tab.close(done);
  147. });
  148. tabs.open({
  149. url: url1,
  150. onOpen: function(tab) {
  151. tab.url = url2;
  152. }
  153. });
  154. };
  155. // TEST: tab.move()
  156. exports.testTabMove = function(assert, done) {
  157. let { loader, messages } = LoaderWithHookedConsole();
  158. let tabs = loader.require('sdk/tabs');
  159. let url = "data:text/html;charset=utf-8,testTabMove";
  160. tabs.open({
  161. url: url,
  162. onOpen: function(tab1) {
  163. assert.ok(tab1.index >= 0, "opening a tab returns a tab w/ valid index");
  164. tabs.open({
  165. url: url,
  166. onOpen: function(tab) {
  167. let i = tab.index;
  168. assert.ok(tab.index > tab1.index, "2nd tab has valid index");
  169. tab.index = 0;
  170. assert.equal(tab.index, i, "tab index after move matches");
  171. assert.equal(JSON.stringify(messages),
  172. JSON.stringify([ERR_FENNEC_MSG]),
  173. "setting tab.index logs error");
  174. // end test
  175. tab1.close(function() tab.close(function() {
  176. loader.unload();
  177. done();
  178. }));
  179. }
  180. });
  181. }
  182. });
  183. };
  184. // TEST: open tab with default options
  185. exports.testTabsOpen_alt = function(assert, done) {
  186. let { loader, messages } = LoaderWithHookedConsole();
  187. let tabs = loader.require('sdk/tabs');
  188. let url = "data:text/html;charset=utf-8,default";
  189. tabs.open({
  190. url: url,
  191. onReady: function(tab) {
  192. assert.equal(tab.url, url, "URL of the new tab matches");
  193. assert.equal(tabs.activeTab, tab, "URL of active tab in the current window matches");
  194. assert.equal(tab.isPinned, false, "The new tab is not pinned");
  195. assert.equal(messages.length, 1, "isPinned logs error");
  196. // end test
  197. tab.close(function() {
  198. loader.unload();
  199. done();
  200. });
  201. }
  202. });
  203. };
  204. // TEST: open pinned tab
  205. exports.testOpenPinned_alt = function(assert, done) {
  206. let { loader, messages } = LoaderWithHookedConsole();
  207. let tabs = loader.require('sdk/tabs');
  208. let url = "about:blank";
  209. tabs.open({
  210. url: url,
  211. isPinned: true,
  212. onOpen: function(tab) {
  213. assert.equal(tab.isPinned, false, "The new tab is pinned");
  214. // We get two error message: one for tabs.open's isPinned argument
  215. // and another one for tab.isPinned
  216. assert.equal(JSON.stringify(messages),
  217. JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
  218. "isPinned logs error");
  219. // end test
  220. tab.close(function() {
  221. loader.unload();
  222. done();
  223. });
  224. }
  225. });
  226. };
  227. // TEST: pin/unpin opened tab
  228. exports.testPinUnpin_alt = function(assert, done) {
  229. let { loader, messages } = LoaderWithHookedConsole();
  230. let tabs = loader.require('sdk/tabs');
  231. let url = "data:text/html;charset=utf-8,default";
  232. tabs.open({
  233. url: url,
  234. onOpen: function(tab) {
  235. tab.pin();
  236. assert.equal(tab.isPinned, false, "The tab was pinned correctly");
  237. assert.equal(JSON.stringify(messages),
  238. JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
  239. "tab.pin() logs error");
  240. // Clear console messages for the following test
  241. messages.length = 0;
  242. tab.unpin();
  243. assert.equal(tab.isPinned, false, "The tab was unpinned correctly");
  244. assert.equal(JSON.stringify(messages),
  245. JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
  246. "tab.unpin() logs error");
  247. // end test
  248. tab.close(function() {
  249. loader.unload();
  250. done();
  251. });
  252. }
  253. });
  254. };
  255. // TEST: open tab in background
  256. exports.testInBackground = function(assert, done) {
  257. let activeUrl = tabs.activeTab.url;
  258. let url = "data:text/html;charset=utf-8,background";
  259. let window = windows.browserWindows.activeWindow;
  260. tabs.once('ready', function onReady(tab) {
  261. assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed");
  262. assert.equal(tab.url, url, "URL of the new background tab matches");
  263. assert.equal(windows.browserWindows.activeWindow, window, "a new window was not opened");
  264. assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL");
  265. // end test
  266. tab.close(done);
  267. });
  268. tabs.open({
  269. url: url,
  270. inBackground: true
  271. });
  272. };
  273. // TEST: open tab in new window
  274. exports.testOpenInNewWindow = function(assert, done) {
  275. let url = "data:text/html;charset=utf-8,newwindow";
  276. let window = windows.browserWindows.activeWindow;
  277. tabs.open({
  278. url: url,
  279. inNewWindow: true,
  280. onReady: function(tab) {
  281. assert.equal(windows.browserWindows.length, 1, "a new window was not opened");
  282. assert.equal(windows.browserWindows.activeWindow, window, "old window is active");
  283. assert.equal(tab.url, url, "URL of the new tab matches");
  284. assert.equal(tabs.activeTab, tab, "tab is the activeTab");
  285. tab.close(done);
  286. }
  287. });
  288. };
  289. // TEST: onOpen event handler
  290. exports.testTabsEvent_onOpen = function(assert, done) {
  291. let url = URL.replace('#title#', 'testTabsEvent_onOpen');
  292. let eventCount = 0;
  293. // add listener via property assignment
  294. function listener1(tab) {
  295. eventCount++;
  296. };
  297. tabs.on('open', listener1);
  298. // add listener via collection add
  299. tabs.on('open', function listener2(tab) {
  300. assert.equal(++eventCount, 2, "both listeners notified");
  301. tabs.removeListener('open', listener1);
  302. tabs.removeListener('open', listener2);
  303. // ends test
  304. tab.close(done);
  305. });
  306. tabs.open(url);
  307. };
  308. // TEST: onClose event handler
  309. exports.testTabsEvent_onClose = function(assert, done) {
  310. let url = "data:text/html;charset=utf-8,onclose";
  311. let eventCount = 0;
  312. // add listener via property assignment
  313. function listener1(tab) {
  314. eventCount++;
  315. }
  316. tabs.on('close', listener1);
  317. // add listener via collection add
  318. tabs.on('close', function listener2(tab) {
  319. assert.equal(++eventCount, 2, "both listeners notified");
  320. tabs.removeListener('close', listener1);
  321. tabs.removeListener('close', listener2);
  322. // end test
  323. done();
  324. });
  325. tabs.on('ready', function onReady(tab) {
  326. tabs.removeListener('ready', onReady);
  327. tab.close();
  328. });
  329. tabs.open(url);
  330. };
  331. // TEST: onClose event handler when a window is closed
  332. exports.testTabsEvent_onCloseWindow = function(assert, done) {
  333. let closeCount = 0, individualCloseCount = 0;
  334. function listener() {
  335. closeCount++;
  336. }
  337. tabs.on('close', listener);
  338. // One tab is already open with the window
  339. let openTabs = 0;
  340. function testCasePossiblyLoaded(tab) {
  341. tab.close(function() {
  342. if (++openTabs == 3) {
  343. tabs.removeListener("close", listener);
  344. assert.equal(closeCount, 3, "Correct number of close events received");
  345. assert.equal(individualCloseCount, 3,
  346. "Each tab with an attached onClose listener received a close " +
  347. "event when the window was closed");
  348. done();
  349. }
  350. });
  351. }
  352. tabs.open({
  353. url: "data:text/html;charset=utf-8,tab2",
  354. onOpen: testCasePossiblyLoaded,
  355. onClose: function() individualCloseCount++
  356. });
  357. tabs.open({
  358. url: "data:text/html;charset=utf-8,tab3",
  359. onOpen: testCasePossiblyLoaded,
  360. onClose: function() individualCloseCount++
  361. });
  362. tabs.open({
  363. url: "data:text/html;charset=utf-8,tab4",
  364. onOpen: testCasePossiblyLoaded,
  365. onClose: function() individualCloseCount++
  366. });
  367. };
  368. // TEST: onReady event handler
  369. exports.testTabsEvent_onReady = function(assert, done) {
  370. let url = "data:text/html;charset=utf-8,onready";
  371. let eventCount = 0;
  372. // add listener via property assignment
  373. function listener1(tab) {
  374. eventCount++;
  375. };
  376. tabs.on('ready', listener1);
  377. // add listener via collection add
  378. tabs.on('ready', function listener2(tab) {
  379. assert.equal(++eventCount, 2, "both listeners notified");
  380. tabs.removeListener('ready', listener1);
  381. tabs.removeListener('ready', listener2);
  382. // end test
  383. tab.close(done);
  384. });
  385. tabs.open(url);
  386. };
  387. // TEST: onActivate event handler
  388. exports.testTabsEvent_onActivate = function(assert, done) {
  389. let url = "data:text/html;charset=utf-8,onactivate";
  390. let eventCount = 0;
  391. // add listener via property assignment
  392. function listener1(tab) {
  393. eventCount++;
  394. };
  395. tabs.on('activate', listener1);
  396. // add listener via collection add
  397. tabs.on('activate', function listener2(tab) {
  398. assert.equal(++eventCount, 2, "both listeners notified");
  399. assert.equal(tab, tabs.activeTab, 'the active tab is correct');
  400. tabs.removeListener('activate', listener1);
  401. tabs.removeListener('activate', listener2);
  402. // end test
  403. tab.close(done);
  404. });
  405. tabs.open(url);
  406. };
  407. // TEST: onDeactivate event handler
  408. exports.testTabsEvent_onDeactivate = function(assert, done) {
  409. let url = "data:text/html;charset=utf-8,ondeactivate";
  410. let eventCount = 0;
  411. // add listener via property assignment
  412. function listener1(tab) {
  413. eventCount++;
  414. };
  415. tabs.on('deactivate', listener1);
  416. // add listener via collection add
  417. tabs.on('deactivate', function listener2(tab) {
  418. assert.equal(++eventCount, 2, 'both listeners notified');
  419. assert.notEqual(tab, tabs.activeTab, 'the active tab is not the deactivated tab');
  420. tabs.removeListener('deactivate', listener1);
  421. tabs.removeListener('deactivate', listener2);
  422. // end test
  423. tab.close(done);
  424. });
  425. tabs.on('activate', function onActivate(tab) {
  426. tabs.removeListener('activate', onActivate);
  427. tabs.open("data:text/html;charset=utf-8,foo");
  428. tab.close();
  429. });
  430. tabs.open(url);
  431. };
  432. // TEST: per-tab event handlers
  433. exports.testPerTabEvents = function(assert, done) {
  434. let eventCount = 0;
  435. tabs.open({
  436. url: "data:text/html;charset=utf-8,foo",
  437. onOpen: function(tab) {
  438. // add listener via property assignment
  439. function listener1() {
  440. eventCount++;
  441. };
  442. tab.on('ready', listener1);
  443. // add listener via collection add
  444. tab.on('ready', function listener2() {
  445. assert.equal(eventCount, 1, "both listeners notified");
  446. tab.removeListener('ready', listener1);
  447. tab.removeListener('ready', listener2);
  448. // end test
  449. tab.close(done);
  450. });
  451. }
  452. });
  453. };
  454. exports.testUniqueTabIds = function(assert, done) {
  455. var tabs = require('sdk/tabs');
  456. var tabIds = {};
  457. var steps = [
  458. function (index) {
  459. tabs.open({
  460. url: "data:text/html;charset=utf-8,foo",
  461. onOpen: function(tab) {
  462. tabIds['tab1'] = tab.id;
  463. next(index);
  464. }
  465. });
  466. },
  467. function (index) {
  468. tabs.open({
  469. url: "data:text/html;charset=utf-8,bar",
  470. onOpen: function(tab) {
  471. tabIds['tab2'] = tab.id;
  472. next(index);
  473. }
  474. });
  475. },
  476. function (index) {
  477. assert.notEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique.");
  478. done();
  479. }
  480. ];
  481. function next(index) {
  482. if (index === steps.length) {
  483. return;
  484. }
  485. let fn = steps[index];
  486. index++;
  487. fn(index);
  488. }
  489. next(0);
  490. }
  491. require('sdk/test').run(exports);