WebPlatform-test.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. Copyright 2022 The Matrix.org Foundation C.I.C.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. import fetchMock from "fetch-mock-jest";
  14. import { UpdateCheckStatus } from "matrix-react-sdk/src/BasePlatform";
  15. import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
  16. import WebPlatform from "../../../../src/vector/platform/WebPlatform";
  17. fetchMock.config.overwriteRoutes = true;
  18. describe("WebPlatform", () => {
  19. beforeEach(() => {
  20. jest.clearAllMocks();
  21. });
  22. it("returns human readable name", () => {
  23. const platform = new WebPlatform();
  24. expect(platform.getHumanReadableName()).toEqual("Web Platform");
  25. });
  26. it("registers service worker", () => {
  27. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  28. // @ts-ignore - mocking readonly object
  29. navigator.serviceWorker = { register: jest.fn() };
  30. new WebPlatform();
  31. expect(navigator.serviceWorker.register).toHaveBeenCalled();
  32. });
  33. it("should call reload on window location object", () => {
  34. delete window.location;
  35. window.location = {
  36. reload: jest.fn(),
  37. } as unknown as Location;
  38. const platform = new WebPlatform();
  39. expect(window.location.reload).not.toHaveBeenCalled();
  40. platform.reload();
  41. expect(window.location.reload).toHaveBeenCalled();
  42. });
  43. it("should call reload to install update", () => {
  44. delete window.location;
  45. window.location = {
  46. reload: jest.fn(),
  47. } as unknown as Location;
  48. const platform = new WebPlatform();
  49. expect(window.location.reload).not.toHaveBeenCalled();
  50. platform.installUpdate();
  51. expect(window.location.reload).toHaveBeenCalled();
  52. });
  53. describe("getDefaultDeviceDisplayName", () => {
  54. it.each([
  55. [
  56. "https://develop.element.io/#/room/!foo:bar",
  57. "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) " +
  58. "Chrome/105.0.0.0 Safari/537.36",
  59. "develop.element.io: Chrome on macOS",
  60. ],
  61. ])("%s & %s = %s", (url, userAgent, result) => {
  62. delete window.navigator;
  63. window.navigator = { userAgent } as unknown as Navigator;
  64. delete window.location;
  65. window.location = { href: url } as unknown as Location;
  66. const platform = new WebPlatform();
  67. expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
  68. });
  69. });
  70. describe("notification support", () => {
  71. const mockNotification = {
  72. requestPermission: jest.fn(),
  73. permission: "notGranted",
  74. };
  75. beforeEach(() => {
  76. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  77. // @ts-ignore
  78. window.Notification = mockNotification;
  79. mockNotification.permission = "notGranted";
  80. });
  81. it("supportsNotifications returns false when platform does not support notifications", () => {
  82. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  83. // @ts-ignore
  84. window.Notification = undefined;
  85. expect(new WebPlatform().supportsNotifications()).toBe(false);
  86. });
  87. it("supportsNotifications returns true when platform supports notifications", () => {
  88. expect(new WebPlatform().supportsNotifications()).toBe(true);
  89. });
  90. it("maySendNotifications returns true when notification permissions are not granted", () => {
  91. expect(new WebPlatform().maySendNotifications()).toBe(false);
  92. });
  93. it("maySendNotifications returns true when notification permissions are granted", () => {
  94. mockNotification.permission = "granted";
  95. expect(new WebPlatform().maySendNotifications()).toBe(true);
  96. });
  97. it("requests notification permissions and returns result ", async () => {
  98. mockNotification.requestPermission.mockImplementation((callback) => callback("test"));
  99. const platform = new WebPlatform();
  100. const result = await platform.requestNotificationPermission();
  101. expect(result).toEqual("test");
  102. });
  103. });
  104. describe("app version", () => {
  105. const envVersion = process.env.VERSION;
  106. const prodVersion = "1.10.13";
  107. beforeEach(() => {
  108. jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(false);
  109. });
  110. afterAll(() => {
  111. process.env.VERSION = envVersion;
  112. });
  113. it("should return true from canSelfUpdate()", async () => {
  114. const platform = new WebPlatform();
  115. const result = await platform.canSelfUpdate();
  116. expect(result).toBe(true);
  117. });
  118. it("getAppVersion returns normalized app version", async () => {
  119. process.env.VERSION = prodVersion;
  120. const platform = new WebPlatform();
  121. const version = await platform.getAppVersion();
  122. expect(version).toEqual(prodVersion);
  123. process.env.VERSION = `v${prodVersion}`;
  124. const version2 = await platform.getAppVersion();
  125. // v prefix removed
  126. expect(version2).toEqual(prodVersion);
  127. process.env.VERSION = `version not like semver`;
  128. const notSemverVersion = await platform.getAppVersion();
  129. expect(notSemverVersion).toEqual(`version not like semver`);
  130. });
  131. describe("pollForUpdate()", () => {
  132. it(
  133. "should return not available and call showNoUpdate when current version " +
  134. "matches most recent version",
  135. async () => {
  136. process.env.VERSION = prodVersion;
  137. fetchMock.getOnce("/version", prodVersion);
  138. const platform = new WebPlatform();
  139. const showUpdate = jest.fn();
  140. const showNoUpdate = jest.fn();
  141. const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
  142. expect(result).toEqual({ status: UpdateCheckStatus.NotAvailable });
  143. expect(showUpdate).not.toHaveBeenCalled();
  144. expect(showNoUpdate).toHaveBeenCalled();
  145. },
  146. );
  147. it("should strip v prefix from versions before comparing", async () => {
  148. process.env.VERSION = prodVersion;
  149. fetchMock.getOnce("/version", `v${prodVersion}`);
  150. const platform = new WebPlatform();
  151. const showUpdate = jest.fn();
  152. const showNoUpdate = jest.fn();
  153. const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
  154. // versions only differ by v prefix, no update
  155. expect(result).toEqual({ status: UpdateCheckStatus.NotAvailable });
  156. expect(showUpdate).not.toHaveBeenCalled();
  157. expect(showNoUpdate).toHaveBeenCalled();
  158. });
  159. it(
  160. "should return ready and call showUpdate when current version " + "differs from most recent version",
  161. async () => {
  162. process.env.VERSION = "0.0.0"; // old version
  163. fetchMock.getOnce("/version", prodVersion);
  164. const platform = new WebPlatform();
  165. const showUpdate = jest.fn();
  166. const showNoUpdate = jest.fn();
  167. const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
  168. expect(result).toEqual({ status: UpdateCheckStatus.Ready });
  169. expect(showUpdate).toHaveBeenCalledWith("0.0.0", prodVersion);
  170. expect(showNoUpdate).not.toHaveBeenCalled();
  171. },
  172. );
  173. it("should return ready without showing update when user registered in last 24", async () => {
  174. process.env.VERSION = "0.0.0"; // old version
  175. jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(true);
  176. fetchMock.getOnce("/version", prodVersion);
  177. const platform = new WebPlatform();
  178. const showUpdate = jest.fn();
  179. const showNoUpdate = jest.fn();
  180. const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
  181. expect(result).toEqual({ status: UpdateCheckStatus.Ready });
  182. expect(showUpdate).not.toHaveBeenCalled();
  183. expect(showNoUpdate).not.toHaveBeenCalled();
  184. });
  185. it("should return error when version check fails", async () => {
  186. fetchMock.getOnce("/version", { throws: "oups" });
  187. const platform = new WebPlatform();
  188. const showUpdate = jest.fn();
  189. const showNoUpdate = jest.fn();
  190. const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
  191. expect(result).toEqual({ status: UpdateCheckStatus.Error, detail: "Unknown Error" });
  192. expect(showUpdate).not.toHaveBeenCalled();
  193. expect(showNoUpdate).not.toHaveBeenCalled();
  194. });
  195. });
  196. });
  197. });