DownloadsManager.test.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. import {actionTypes as at} from "common/Actions.jsm";
  2. import {DownloadsManager} from "lib/DownloadsManager.jsm";
  3. import {GlobalOverrider} from "test/unit/utils";
  4. describe("Downloads Manager", () => {
  5. let downloadsManager;
  6. let globals;
  7. const DOWNLOAD_URL = "https://site.com/download.mov";
  8. beforeEach(() => {
  9. globals = new GlobalOverrider();
  10. global.Cc["@mozilla.org/timer;1"] = {
  11. createInstance() {
  12. return {
  13. initWithCallback: sinon.stub().callsFake(callback => callback()),
  14. cancel: sinon.spy(),
  15. };
  16. },
  17. };
  18. globals.set("DownloadsCommon", {
  19. getData: sinon.stub().returns({
  20. addView: sinon.stub(),
  21. removeView: sinon.stub(),
  22. }),
  23. copyDownloadLink: sinon.stub(),
  24. deleteDownload: sinon.stub().returns(Promise.resolve()),
  25. openDownloadedFile: sinon.stub(),
  26. showDownloadedFile: sinon.stub(),
  27. });
  28. downloadsManager = new DownloadsManager();
  29. downloadsManager.init({dispatch() {}});
  30. downloadsManager.onDownloadAdded({
  31. source: {url: DOWNLOAD_URL},
  32. endTime: Date.now(),
  33. target: {path: "/path/to/download.mov", exists: true},
  34. succeeded: true,
  35. refresh: async () => {},
  36. });
  37. assert.ok(downloadsManager._downloadItems.has(DOWNLOAD_URL));
  38. });
  39. afterEach(() => {
  40. downloadsManager._downloadItems.clear();
  41. globals.restore();
  42. });
  43. describe("#init", () => {
  44. it("should add a DownloadsCommon view on init", () => {
  45. downloadsManager.init({dispatch() {}});
  46. assert.calledTwice(global.DownloadsCommon.getData().addView);
  47. });
  48. });
  49. describe("#onAction", () => {
  50. it("should copy the file on COPY_DOWNLOAD_LINK", () => {
  51. downloadsManager.onAction({type: at.COPY_DOWNLOAD_LINK, data: {url: DOWNLOAD_URL}});
  52. assert.calledOnce(global.DownloadsCommon.copyDownloadLink);
  53. });
  54. it("should remove the file on REMOVE_DOWNLOAD_FILE", () => {
  55. downloadsManager.onAction({type: at.REMOVE_DOWNLOAD_FILE, data: {url: DOWNLOAD_URL}});
  56. assert.calledOnce(global.DownloadsCommon.deleteDownload);
  57. });
  58. it("should show the file on SHOW_DOWNLOAD_FILE", () => {
  59. downloadsManager.onAction({type: at.SHOW_DOWNLOAD_FILE, data: {url: DOWNLOAD_URL}});
  60. assert.calledOnce(global.DownloadsCommon.showDownloadedFile);
  61. });
  62. it("should open the file on OPEN_DOWNLOAD_FILE if the type is download", () => {
  63. downloadsManager.onAction({type: at.OPEN_DOWNLOAD_FILE, data: {url: DOWNLOAD_URL, type: "download"}});
  64. assert.calledOnce(global.DownloadsCommon.openDownloadedFile);
  65. });
  66. it("should copy the file on UNINIT", () => {
  67. // DownloadsManager._downloadData needs to exist first
  68. downloadsManager.onAction({type: at.UNINIT});
  69. assert.calledOnce(global.DownloadsCommon.getData().removeView);
  70. });
  71. it("should not execute a download command if we do not have the correct url", () => {
  72. downloadsManager.onAction({type: at.SHOW_DOWNLOAD_FILE, data: {url: "unknown_url"}});
  73. assert.notCalled(global.DownloadsCommon.showDownloadedFile);
  74. });
  75. });
  76. describe("#onDownloadAdded", () => {
  77. let newDownload;
  78. beforeEach(() => {
  79. downloadsManager._downloadItems.clear();
  80. newDownload = {
  81. source: {url: "https://site.com/newDownload.mov"},
  82. endTime: Date.now(),
  83. target: {path: "/path/to/newDownload.mov", exists: true},
  84. succeeded: true,
  85. refresh: async () => {},
  86. };
  87. });
  88. afterEach(() => {
  89. downloadsManager._downloadItems.clear();
  90. });
  91. it("should add a download on onDownloadAdded", () => {
  92. downloadsManager.onDownloadAdded(newDownload);
  93. assert.ok(downloadsManager._downloadItems.has("https://site.com/newDownload.mov"));
  94. });
  95. it("should not add a download if it already exists", () => {
  96. downloadsManager.onDownloadAdded(newDownload);
  97. downloadsManager.onDownloadAdded(newDownload);
  98. downloadsManager.onDownloadAdded(newDownload);
  99. downloadsManager.onDownloadAdded(newDownload);
  100. const results = downloadsManager._downloadItems;
  101. assert.equal(results.size, 1);
  102. });
  103. it("should not return any downloads if no threshold is provided", async () => {
  104. downloadsManager.onDownloadAdded(newDownload);
  105. const results = await downloadsManager.getDownloads(null, {});
  106. assert.equal(results.length, 0);
  107. });
  108. it("should stop at numItems when it found one it's looking for", async () => {
  109. const aDownload = {
  110. source: {url: "https://site.com/aDownload.pdf"},
  111. endTime: Date.now(),
  112. target: {path: "/path/to/aDownload.pdf", exists: true},
  113. succeeded: true,
  114. refresh: async () => {},
  115. };
  116. downloadsManager.onDownloadAdded(aDownload);
  117. downloadsManager.onDownloadAdded(newDownload);
  118. const results = await downloadsManager.getDownloads(Infinity, {numItems: 1, onlySucceeded: true, onlyExists: true});
  119. assert.equal(results.length, 1);
  120. assert.equal(results[0].url, aDownload.source.url);
  121. });
  122. it("should get all the downloads younger than the threshold provided", async () => {
  123. const oldDownload = {
  124. source: {url: "https://site.com/oldDownload.pdf"},
  125. endTime: Date.now() - 40 * 60 * 60 * 1000,
  126. target: {path: "/path/to/oldDownload.pdf", exists: true},
  127. succeeded: true,
  128. refresh: async () => {},
  129. };
  130. // Add an old download (older than 36 hours in this case)
  131. downloadsManager.onDownloadAdded(oldDownload);
  132. downloadsManager.onDownloadAdded(newDownload);
  133. const RECENT_DOWNLOAD_THRESHOLD = 36 * 60 * 60 * 1000;
  134. const results = await downloadsManager.getDownloads(RECENT_DOWNLOAD_THRESHOLD, {numItems: 5, onlySucceeded: true, onlyExists: true});
  135. assert.equal(results.length, 1);
  136. assert.equal(results[0].url, newDownload.source.url);
  137. });
  138. it("should dispatch DOWNLOAD_CHANGED when adding a download", () => {
  139. downloadsManager._store.dispatch = sinon.spy();
  140. downloadsManager._downloadTimer = null; // Nuke the timer
  141. downloadsManager.onDownloadAdded(newDownload);
  142. assert.calledOnce(downloadsManager._store.dispatch);
  143. });
  144. it("should refresh the downloads if onlyExists is true", async () => {
  145. const aDownload = {
  146. source: {url: "https://site.com/aDownload.pdf"},
  147. endTime: Date.now() - 40 * 60 * 60 * 1000,
  148. target: {path: "/path/to/aDownload.pdf", exists: true},
  149. succeeded: true,
  150. refresh: () => {},
  151. };
  152. sinon.stub(aDownload, "refresh").returns(Promise.resolve());
  153. downloadsManager.onDownloadAdded(aDownload);
  154. await downloadsManager.getDownloads(Infinity, {numItems: 5, onlySucceeded: true, onlyExists: true});
  155. assert.calledOnce(aDownload.refresh);
  156. });
  157. it("should not refresh the downloads if onlyExists is false (by default)", async () => {
  158. const aDownload = {
  159. source: {url: "https://site.com/aDownload.pdf"},
  160. endTime: Date.now() - 40 * 60 * 60 * 1000,
  161. target: {path: "/path/to/aDownload.pdf", exists: true},
  162. succeeded: true,
  163. refresh: () => {},
  164. };
  165. sinon.stub(aDownload, "refresh").returns(Promise.resolve());
  166. downloadsManager.onDownloadAdded(aDownload);
  167. await downloadsManager.getDownloads(Infinity, {numItems: 5, onlySucceeded: true});
  168. assert.notCalled(aDownload.refresh);
  169. });
  170. it("should only return downloads that exist if specified", async () => {
  171. const nonExistantDownload = {
  172. source: {url: "https://site.com/nonExistantDownload.pdf"},
  173. endTime: Date.now() - 40 * 60 * 60 * 1000,
  174. target: {path: "/path/to/nonExistantDownload.pdf", exists: false},
  175. succeeded: true,
  176. refresh: async () => {},
  177. };
  178. downloadsManager.onDownloadAdded(newDownload);
  179. downloadsManager.onDownloadAdded(nonExistantDownload);
  180. const results = await downloadsManager.getDownloads(Infinity, {numItems: 5, onlySucceeded: true, onlyExists: true});
  181. assert.equal(results.length, 1);
  182. assert.equal(results[0].url, newDownload.source.url);
  183. });
  184. it("should return all downloads that either exist or don't exist if not specified", async () => {
  185. const nonExistantDownload = {
  186. source: {url: "https://site.com/nonExistantDownload.pdf"},
  187. endTime: Date.now() - 40 * 60 * 60 * 1000,
  188. target: {path: "/path/to/nonExistantDownload.pdf", exists: false},
  189. succeeded: true,
  190. refresh: async () => {},
  191. };
  192. downloadsManager.onDownloadAdded(newDownload);
  193. downloadsManager.onDownloadAdded(nonExistantDownload);
  194. const results = await downloadsManager.getDownloads(Infinity, {numItems: 5, onlySucceeded: true});
  195. assert.equal(results.length, 2);
  196. assert.equal(results[0].url, newDownload.source.url);
  197. assert.equal(results[1].url, nonExistantDownload.source.url);
  198. });
  199. it("should only return downloads that were successful if specified", async () => {
  200. const nonSuccessfulDownload = {
  201. source: {url: "https://site.com/nonSuccessfulDownload.pdf"},
  202. endTime: Date.now() - 40 * 60 * 60 * 1000,
  203. target: {path: "/path/to/nonSuccessfulDownload.pdf", exists: false},
  204. succeeded: false,
  205. refresh: async () => {},
  206. };
  207. downloadsManager.onDownloadAdded(newDownload);
  208. downloadsManager.onDownloadAdded(nonSuccessfulDownload);
  209. const results = await downloadsManager.getDownloads(Infinity, {numItems: 5, onlySucceeded: true});
  210. assert.equal(results.length, 1);
  211. assert.equal(results[0].url, newDownload.source.url);
  212. });
  213. it("should return all downloads that were either successful or not if not specified", async () => {
  214. const nonExistantDownload = {
  215. source: {url: "https://site.com/nonExistantDownload.pdf"},
  216. endTime: Date.now() - 40 * 60 * 60 * 1000,
  217. target: {path: "/path/to/nonExistantDownload.pdf", exists: true},
  218. succeeded: false,
  219. refresh: async () => {},
  220. };
  221. downloadsManager.onDownloadAdded(newDownload);
  222. downloadsManager.onDownloadAdded(nonExistantDownload);
  223. const results = await downloadsManager.getDownloads(Infinity, {numItems: 5});
  224. assert.equal(results.length, 2);
  225. assert.equal(results[0].url, newDownload.source.url);
  226. assert.equal(results[1].url, nonExistantDownload.source.url);
  227. });
  228. it("should sort the downloads by recency", async () => {
  229. const olderDownload1 = {
  230. source: {url: "https://site.com/oldDownload1.pdf"},
  231. endTime: Date.now() - 2 * 60 * 60 * 1000, // 2 hours ago
  232. target: {path: "/path/to/oldDownload1.pdf", exists: true},
  233. succeeded: true,
  234. refresh: async () => {},
  235. };
  236. const olderDownload2 = {
  237. source: {url: "https://site.com/oldDownload2.pdf"},
  238. endTime: Date.now() - 60 * 60 * 1000, // 1 hour ago
  239. target: {path: "/path/to/oldDownload2.pdf", exists: true},
  240. succeeded: true,
  241. refresh: async () => {},
  242. };
  243. // Add some older downloads and check that they are in order
  244. downloadsManager.onDownloadAdded(olderDownload1);
  245. downloadsManager.onDownloadAdded(olderDownload2);
  246. downloadsManager.onDownloadAdded(newDownload);
  247. const results = await downloadsManager.getDownloads(Infinity, {numItems: 5, onlySucceeded: true, onlyExists: true});
  248. assert.equal(results.length, 3);
  249. assert.equal(results[0].url, newDownload.source.url);
  250. assert.equal(results[1].url, olderDownload2.source.url);
  251. assert.equal(results[2].url, olderDownload1.source.url);
  252. });
  253. it("should format the description properly if there is no file type", async () => {
  254. newDownload.target.path = null;
  255. downloadsManager.onDownloadAdded(newDownload);
  256. const results = await downloadsManager.getDownloads(Infinity, {numItems: 5, onlySucceeded: true, onlyExists: true});
  257. assert.equal(results.length, 1);
  258. assert.equal(results[0].description, "1.5 MB"); // see unit-entry.js to see where this comes from
  259. });
  260. });
  261. describe("#onDownloadRemoved", () => {
  262. let newDownload;
  263. beforeEach(() => {
  264. downloadsManager._downloadItems.clear();
  265. newDownload = {
  266. source: {url: "https://site.com/removeMe.mov"},
  267. endTime: Date.now(),
  268. target: {path: "/path/to/removeMe.mov", exists: true},
  269. succeeded: true,
  270. refresh: async () => {},
  271. };
  272. downloadsManager.onDownloadAdded(newDownload);
  273. });
  274. it("should remove a download if it exists on onDownloadRemoved", async () => {
  275. downloadsManager.onDownloadRemoved({source: {url: "https://site.com/removeMe.mov"}});
  276. const results = await downloadsManager.getDownloads(Infinity, {numItems: 5});
  277. assert.deepEqual(results, []);
  278. });
  279. it("should dispatch DOWNLOAD_CHANGED when removing a download", () => {
  280. downloadsManager._store.dispatch = sinon.spy();
  281. downloadsManager.onDownloadRemoved({source: {url: "https://site.com/removeMe.mov"}});
  282. assert.calledOnce(downloadsManager._store.dispatch);
  283. });
  284. });
  285. });