@omega.js/desktop 0.52.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/README.md +17 -12
  2. package/dist/assets/css/core/_initialize.scss +1 -1
  3. package/dist/assets/js/core/app-shell.js +14 -15
  4. package/dist/assets/themes/_template/_theme.js +6 -6
  5. package/dist/assets/themes/base/_includes/global/sections/account.html +4 -4
  6. package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +5 -5
  7. package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +3 -3
  8. package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +1 -1
  9. package/dist/assets/themes/base/_layouts/frontend/pages/blog/tags/tag.html +1 -1
  10. package/dist/assets/themes/base/_layouts/frontend/pages/payment/confirmation.html +1 -1
  11. package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.js +2 -3
  12. package/dist/assets/themes/base/_sections/verts/unit/section.html +1 -1
  13. package/dist/assets/themes/base/_sections/verts/unit/section.js +3 -4
  14. package/dist/assets/themes/base/_theme.js +4 -3
  15. package/dist/assets/themes/bootstrap/_theme.js +2 -2
  16. package/dist/assets/themes/bootstrap/overrides/_links.scss +1 -1
  17. package/dist/assets/themes/classy/_theme.js +8 -6
  18. package/dist/assets/themes/classy/css/marketing/_sections.scss +1 -2
  19. package/dist/assets/themes/classy/js/hero-demo-form.js +3 -2
  20. package/dist/assets/themes/neobrutalism/_theme.js +5 -5
  21. package/dist/assets/themes/neobrutalism/js/pages/test/libraries/layers/index.js +1 -1
  22. package/dist/assets/themes/newsflash/_theme.js +6 -6
  23. package/dist/assets/themes/newsflash/js/pages/test/libraries/layers/index.js +1 -1
  24. package/dist/build.js +87 -111
  25. package/dist/cli.js +1 -1
  26. package/dist/commands/build.js +2 -2
  27. package/dist/commands/cdp/capture.js +2 -2
  28. package/dist/commands/cdp/quit.js +2 -2
  29. package/dist/commands/cdp/relaunch.js +2 -2
  30. package/dist/commands/cdp/theme.js +1 -1
  31. package/dist/commands/clean.js +2 -2
  32. package/dist/commands/deploy.js +4 -4
  33. package/dist/commands/finalize-release.js +4 -4
  34. package/dist/commands/install.js +3 -3
  35. package/dist/commands/launch.js +2 -2
  36. package/dist/commands/lib/deploy-precheck.js +3 -3
  37. package/dist/commands/lib/ensure-target.js +6 -6
  38. package/dist/commands/package.js +2 -2
  39. package/dist/commands/publish.js +2 -2
  40. package/dist/commands/release.js +3 -3
  41. package/dist/commands/runner.js +11 -11
  42. package/dist/commands/sign-windows.js +5 -5
  43. package/dist/commands/test.js +4 -4
  44. package/dist/commands/update.js +2 -2
  45. package/dist/commands/validate-certs.js +4 -4
  46. package/dist/commands/version.js +3 -3
  47. package/dist/defaults/AGENTS.md +17 -8
  48. package/dist/defaults/config/omega.json5 +7 -7
  49. package/dist/defaults/hooks/build/post.js +1 -1
  50. package/dist/defaults/hooks/build/pre.js +1 -1
  51. package/dist/defaults/hooks/deploy/pre.js +1 -1
  52. package/dist/defaults/hooks/release/post.js +1 -1
  53. package/dist/defaults/hooks/release/pre.js +1 -1
  54. package/dist/defaults/src/assets/js/components/about/index.js +3 -5
  55. package/dist/defaults/src/assets/js/components/main/index.js +3 -5
  56. package/dist/defaults/src/assets/js/components/settings/index.js +3 -5
  57. package/dist/defaults/src/integrations/context-menu/index.js +2 -2
  58. package/dist/defaults/src/integrations/menu/index.js +3 -3
  59. package/dist/defaults/src/integrations/tray/index.js +3 -3
  60. package/dist/defaults/src/main.js +3 -5
  61. package/dist/defaults/src/preload.js +3 -5
  62. package/dist/defaults/test/README.md +2 -2
  63. package/dist/gulp/main.js +9 -10
  64. package/dist/gulp/tasks/audit.js +7 -7
  65. package/dist/gulp/tasks/build-config.js +8 -8
  66. package/dist/gulp/tasks/bundle.js +16 -16
  67. package/dist/gulp/tasks/defaults.js +3 -3
  68. package/dist/gulp/tasks/distribute.js +2 -2
  69. package/dist/gulp/tasks/html.js +9 -9
  70. package/dist/gulp/tasks/package-quick.js +3 -3
  71. package/dist/gulp/tasks/package.js +3 -3
  72. package/dist/gulp/tasks/release.js +3 -3
  73. package/dist/gulp/tasks/sass.js +6 -6
  74. package/dist/gulp/tasks/serve.js +4 -4
  75. package/dist/hooks/notarize-artifacts.js +1 -1
  76. package/dist/hooks/notarize.js +1 -1
  77. package/dist/index.js +5 -8
  78. package/dist/lib/_environment-mixin.js +50 -0
  79. package/dist/lib/_lifecycle-mixin.js +45 -0
  80. package/dist/lib/analytics.js +33 -35
  81. package/dist/lib/app-state.js +14 -14
  82. package/dist/lib/auth-flow.js +18 -18
  83. package/dist/lib/auth-persistence.js +12 -12
  84. package/dist/lib/auth.js +421 -0
  85. package/dist/lib/auto-updater.js +49 -49
  86. package/dist/lib/context-menu.js +13 -13
  87. package/dist/lib/context.js +19 -19
  88. package/dist/lib/deep-link.js +34 -34
  89. package/dist/lib/fontawesome.js +5 -5
  90. package/dist/lib/ipc.js +4 -4
  91. package/dist/lib/menu.js +25 -25
  92. package/dist/lib/protocol.js +5 -5
  93. package/dist/lib/remote-config.js +22 -22
  94. package/dist/lib/remote-scripts.js +21 -21
  95. package/dist/lib/restart-manager/index.js +28 -28
  96. package/dist/lib/sign-helpers/exec-with-limit.js +1 -1
  97. package/dist/lib/sign-helpers/sign-events.js +1 -1
  98. package/dist/lib/startup.js +18 -13
  99. package/dist/lib/storage.js +10 -10
  100. package/dist/lib/templating.js +16 -16
  101. package/dist/lib/theme.js +10 -10
  102. package/dist/lib/tray.js +27 -27
  103. package/dist/lib/usage.js +11 -11
  104. package/dist/lib/window-manager.js +26 -26
  105. package/dist/main.js +398 -483
  106. package/dist/preload.js +236 -176
  107. package/dist/renderer.js +417 -391
  108. package/dist/test/fixtures/consumer-app/config/omega.json5 +1 -1
  109. package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +8 -11
  110. package/dist/test/fixtures/consumer-app/src/main.js +5 -7
  111. package/dist/test/fixtures/consumer-app/src/preload.js +2 -2
  112. package/dist/test/harness/boot-entry.js +22 -20
  113. package/dist/test/harness/main-entry.js +31 -30
  114. package/dist/test/harness/renderer-entry.js +5 -5
  115. package/dist/test/harness/renderer-preload.js +137 -141
  116. package/dist/test/index.js +10 -10
  117. package/dist/test/runner.js +2 -2
  118. package/dist/test/runners/boot.js +7 -6
  119. package/dist/test/runners/electron.js +3 -2
  120. package/dist/test/runners/render-event.js +2 -2
  121. package/dist/test/suites/boot/consumer-app-boots.test.js +57 -26
  122. package/dist/test/suites/boot/restart-manager.test.js +2 -2
  123. package/dist/test/suites/boot/storage-bundled.test.js +5 -5
  124. package/dist/test/suites/boot/theme.test.js +13 -13
  125. package/dist/test/suites/build/audit.test.js +1 -1
  126. package/dist/test/suites/build/auth-persistence-resolve.test.js +6 -6
  127. package/dist/test/suites/build/boot-fixture.test.js +2 -2
  128. package/dist/test/suites/build/boot-runner-timeout.test.js +6 -5
  129. package/dist/test/suites/build/brand-scss.test.js +1 -1
  130. package/dist/test/suites/build/build-json-bake.test.js +1 -1
  131. package/dist/test/suites/build/cli.test.js +2 -2
  132. package/dist/test/suites/build/config-schema.test.js +5 -5
  133. package/dist/test/suites/build/defaults-scaffold.test.js +2 -2
  134. package/dist/test/suites/build/deploy-hook.test.js +3 -3
  135. package/dist/test/suites/build/ensure-target.test.js +2 -2
  136. package/dist/test/suites/build/env-delivery.test.js +2 -2
  137. package/dist/test/suites/build/esm-only-dependency.test.js +2 -2
  138. package/dist/test/suites/build/exports.test.js +9 -8
  139. package/dist/test/suites/build/get-config.test.js +4 -4
  140. package/dist/test/suites/build/manifest-deps.test.js +1 -1
  141. package/dist/test/suites/build/merge-line-files.test.js +1 -1
  142. package/dist/test/suites/build/omega-shell.test.js +34 -2
  143. package/dist/test/suites/build/omega.test.js +350 -0
  144. package/dist/test/suites/build/renderer-auth-bridge.test.js +211 -78
  145. package/dist/test/suites/build/runner.test.js +2 -2
  146. package/dist/test/suites/build/sentry.test.js +2 -2
  147. package/dist/test/suites/build/sign-windows-e2e.test.js +2 -2
  148. package/dist/test/suites/build/templating.test.js +3 -3
  149. package/dist/test/suites/build/test-stealth.test.js +7 -9
  150. package/dist/test/suites/build/url-helpers.test.js +55 -56
  151. package/dist/test/suites/build/validate-config.test.js +2 -2
  152. package/dist/test/suites/build/wave5-pins.test.js +2 -2
  153. package/dist/test/suites/main/analytics.test.js +59 -59
  154. package/dist/test/suites/main/app-state.test.js +66 -66
  155. package/dist/test/suites/main/auth-flow.test.js +41 -41
  156. package/dist/test/suites/main/auth-persistence.test.js +54 -43
  157. package/dist/test/suites/main/{client-bridge.integration.test.js → auth.integration.test.js} +10 -9
  158. package/dist/test/suites/main/auth.test.js +336 -0
  159. package/dist/test/suites/main/auto-updater.test.js +134 -134
  160. package/dist/test/suites/main/boot-sequence.test.js +37 -49
  161. package/dist/test/suites/main/context-menu.test.js +51 -50
  162. package/dist/test/suites/main/context.test.js +25 -25
  163. package/dist/test/suites/main/deep-link.test.js +74 -74
  164. package/dist/test/suites/main/fontawesome.test.js +27 -27
  165. package/dist/test/suites/main/ipc.test.js +35 -35
  166. package/dist/test/suites/main/menu.test.js +101 -100
  167. package/dist/test/suites/main/protocol.test.js +19 -19
  168. package/dist/test/suites/main/remote-config.test.js +63 -63
  169. package/dist/test/suites/main/remote-scripts.test.js +103 -103
  170. package/dist/test/suites/main/request.test.js +71 -0
  171. package/dist/test/suites/main/restart-manager.test.js +33 -33
  172. package/dist/test/suites/main/startup-paths-and-ua.test.js +4 -4
  173. package/dist/test/suites/main/startup.test.js +26 -26
  174. package/dist/test/suites/main/stealth-window.test.js +1 -1
  175. package/dist/test/suites/main/storage.test.js +25 -25
  176. package/dist/test/suites/main/theme.test.js +34 -34
  177. package/dist/test/suites/main/tray.test.js +79 -79
  178. package/dist/test/suites/main/url-helpers.test.js +133 -133
  179. package/dist/test/suites/main/usage.test.js +25 -25
  180. package/dist/test/suites/main/window-bounds.test.js +27 -27
  181. package/dist/test/suites/main/window-manager.test.js +44 -44
  182. package/dist/test/suites/renderer/analytics-bridge.test.js +5 -5
  183. package/dist/test/suites/renderer/cross-context-helpers.test.js +37 -31
  184. package/dist/test/suites/renderer/round-trip.test.js +3 -3
  185. package/dist/test/suites/renderer/tooltips.test.js +15 -15
  186. package/dist/test/suites/renderer/{window-em-surface.test.js → window-desktop-surface.test.js} +27 -7
  187. package/dist/test/utils/extended-mode-warning.js +1 -1
  188. package/dist/utils/boot-harness.js +56 -0
  189. package/dist/utils/mode-helpers.js +2 -15
  190. package/dist/utils/ship-keys.js +3 -3
  191. package/dist/utils/signing-status.js +51 -0
  192. package/dist/utils/test-events.js +7 -0
  193. package/dist/utils/test-stealth.js +6 -6
  194. package/dist/utils/url-helpers.js +52 -42
  195. package/dist/utils/user-agent.js +44 -0
  196. package/dist/vendor/account/engine.js +3 -3
  197. package/dist/vendor/account/index.js +14 -45
  198. package/dist/vendor/account/resolve-account.js +44 -0
  199. package/dist/vendor/account/schema.js +1 -1
  200. package/dist/vendor/account/user.js +99 -0
  201. package/dist/vendor/config/client-config.js +1 -1
  202. package/dist/vendor/config/environment.js +11 -30
  203. package/dist/vendor/config/index.js +8 -11
  204. package/dist/vendor/config/load.js +1 -2
  205. package/dist/vendor/config/platforms.js +1 -1
  206. package/dist/vendor/config/retired-keys.js +2 -2
  207. package/dist/vendor/config/schema.js +5 -8
  208. package/dist/vendor/config/site-global.js +2 -3
  209. package/dist/vendor/config/validate.js +1 -2
  210. package/dist/vendor/config/winback.js +1 -1
  211. package/dist/vendor/devkit/actions-secrets.js +1 -1
  212. package/dist/vendor/devkit/attach-log-file.js +1 -1
  213. package/dist/vendor/devkit/build-json.js +1 -1
  214. package/dist/vendor/devkit/cli-router.js +3 -4
  215. package/dist/vendor/devkit/defaults-engine.js +9 -11
  216. package/dist/vendor/devkit/local.js +2 -0
  217. package/dist/vendor/devkit/merge-line-files.js +2 -3
  218. package/dist/vendor/devkit/test/runner-core.js +6 -6
  219. package/dist/vendor/monitoring/env.js +2 -2
  220. package/dist/vendor/monitoring/index.js +1 -1
  221. package/dist/vendor/monitoring/main.js +1 -1
  222. package/dist/vendor/monitoring/preload.js +1 -1
  223. package/dist/vendor/monitoring/renderer.js +1 -1
  224. package/docs/analytics.md +8 -8
  225. package/docs/app-state.md +19 -19
  226. package/docs/audit.md +4 -4
  227. package/docs/{client-bridge.md → auth.md} +88 -73
  228. package/docs/auto-updater.md +8 -8
  229. package/docs/boot-sequence.md +11 -6
  230. package/docs/build-system.md +1 -1
  231. package/docs/cdp-debugging.md +1 -1
  232. package/docs/common-mistakes.md +7 -7
  233. package/docs/config-schema.md +1 -1
  234. package/docs/context-menu.md +13 -13
  235. package/docs/context.md +11 -11
  236. package/docs/css.md +3 -9
  237. package/docs/deep-link.md +25 -25
  238. package/docs/environment-detection.md +16 -16
  239. package/docs/fontawesome.md +7 -5
  240. package/docs/hooks.md +8 -8
  241. package/docs/index.md +38 -27
  242. package/docs/ipc.md +10 -10
  243. package/docs/lib-modules.md +9 -9
  244. package/docs/logging.md +12 -14
  245. package/docs/menu.md +18 -18
  246. package/docs/remote-config.md +9 -9
  247. package/docs/remote-scripts.md +12 -12
  248. package/docs/restart-manager.md +8 -8
  249. package/docs/sentry.md +4 -4
  250. package/docs/shared/analytics.md +1 -1
  251. package/docs/shared/breaking-changes.md +79 -13
  252. package/docs/shared/config.md +17 -21
  253. package/docs/shared/logging.md +1 -1
  254. package/docs/shared/monitoring.md +5 -5
  255. package/docs/shared/testing.md +2 -2
  256. package/docs/shared/theming.md +1 -1
  257. package/docs/shared/translation.md +19 -10
  258. package/docs/startup.md +16 -16
  259. package/docs/storage.md +11 -11
  260. package/docs/templating.md +3 -3
  261. package/docs/test-boot-layer.md +12 -12
  262. package/docs/test-framework.md +17 -17
  263. package/docs/themes.md +7 -7
  264. package/docs/tooltips.md +2 -2
  265. package/docs/tray.md +31 -31
  266. package/docs/usage.md +7 -7
  267. package/docs/verts.md +1 -1
  268. package/docs/windows.md +22 -22
  269. package/package.json +3 -4
  270. package/dist/lib/client-bridge.js +0 -374
  271. package/dist/lib/logger.js +0 -4
  272. package/dist/test/suites/build/manager.test.js +0 -213
  273. package/dist/test/suites/main/client-bridge.test.js +0 -262
@@ -0,0 +1,336 @@
1
+ // Main-process tests for lib/auth.js (`omega.auth`): unit-level coverage that doesn't hit Firebase.
2
+ //
3
+ // Real Firebase integration tests live in auth.integration.test.js
4
+ // (gated on extended mode); the sign-in proof itself is #904's, through a
5
+ // persona the backend emulator seeds.
6
+
7
+ const { User } = require('../../../vendor/account/index.js');
8
+ const defineCases = require('../../../vendor/devkit/test/define-cases.js');
9
+
10
+ module.exports = defineCases({
11
+ type: 'suite',
12
+ layer: 'main',
13
+ description: 'auth (main, unit)',
14
+ cleanup: async (ctx) => {
15
+ await ctx.omega.auth._resetForTests();
16
+ },
17
+ tests: [
18
+ {
19
+ name: 'initialize ran during boot',
20
+ run: (ctx) => {
21
+ ctx.expect(ctx.omega.auth._initialized).toBe(true);
22
+ },
23
+ },
24
+ {
25
+ name: 'IPC handlers are registered',
26
+ run: (ctx) => {
27
+ ctx.expect(ctx.omega.ipc.hasHandler('desktop:auth:sync-request')).toBe(true);
28
+ ctx.expect(ctx.omega.ipc.hasHandler('desktop:auth:sign-out')).toBe(true);
29
+ ctx.expect(ctx.omega.ipc.hasHandler('desktop:auth:get-user')).toBe(true);
30
+ },
31
+ },
32
+ {
33
+ // The public surface is exactly the account, its listener, and the three
34
+ // session calls; the snapshot/plan/roles getters and onAuthChange retired
35
+ // into `user` and `listen`.
36
+ name: 'the public surface: user, listen, signOut, getIdToken, handleToken',
37
+ run: (ctx) => {
38
+ const auth = ctx.omega.auth;
39
+ for (const name of ['listen', 'signOut', 'getIdToken', 'handleToken']) {
40
+ ctx.expect(typeof auth[name]).toBe('function');
41
+ }
42
+ for (const name of ['getCurrentUser', 'getResolvedPlan', 'getResolvedRoles', 'onAuthChange', 'handleAuthToken']) {
43
+ ctx.expect(auth[name]).toBeUndefined();
44
+ }
45
+ },
46
+ },
47
+ {
48
+ name: 'user is a signed-out User when nothing is signed in',
49
+ run: (ctx) => {
50
+ // The harness boots without auth; either firebase isn't loaded (no cloud.config
51
+ // in default config) or it is and there's no user. Either way, no renderer has
52
+ // pushed an account, so `user` is the signed-out User.
53
+ const user = ctx.omega.auth.user;
54
+ ctx.expect(user).toBeInstanceOf(User);
55
+ ctx.expect(user.authenticated).toBe(false);
56
+ ctx.expect(user.uid).toBeNull();
57
+ ctx.expect(user.plan).toBe('basic');
58
+ },
59
+ },
60
+ {
61
+ name: 'getIdToken returns null when nothing is signed in',
62
+ run: async (ctx) => {
63
+ // Same no-auth harness as the user case above: signed-in token
64
+ // round-trips are the integration suite's domain.
65
+ ctx.expect(await ctx.omega.auth.getIdToken()).toBeNull();
66
+ },
67
+ },
68
+ {
69
+ name: 'handleToken returns no-op result when firebase not loaded',
70
+ run: async (ctx) => {
71
+ // Default config has empty cloud.config → firebase fails to init → _firebaseAuth=null.
72
+ if (ctx.omega.auth._firebaseAuth) {
73
+ ctx.skip('firebase did load (cloud.config was set) — covered by integration tests');
74
+ }
75
+ const r = await ctx.omega.auth.handleToken('whatever');
76
+ ctx.expect(r.success).toBe(false);
77
+ ctx.expect(r.reason).toBe('firebase-not-loaded');
78
+ },
79
+ },
80
+ {
81
+ name: 'handleToken with empty token returns no-token failure',
82
+ run: async (ctx) => {
83
+ // Stub _firebaseAuth so we hit the "no token" branch instead of "firebase-not-loaded."
84
+ const origAuth = ctx.omega.auth._firebaseAuth;
85
+ ctx.omega.auth._firebaseAuth = { currentUser: null }; // truthy so we pass the firebase check
86
+ try {
87
+ const r = await ctx.omega.auth.handleToken('');
88
+ ctx.expect(r.success).toBe(false);
89
+ ctx.expect(r.reason).toBe('no-token');
90
+ } finally {
91
+ ctx.omega.auth._firebaseAuth = origAuth;
92
+ }
93
+ },
94
+ },
95
+ {
96
+ name: 'sync-request: same UID → no sync needed',
97
+ run: async (ctx) => {
98
+ // Stub the auth so we control bgUid.
99
+ const origAuth = ctx.omega.auth._firebaseAuth;
100
+ ctx.omega.auth._firebaseAuth = { currentUser: { uid: 'abc' } };
101
+ try {
102
+ const result = await ctx.omega.ipc.invoke('desktop:auth:sync-request', { contextUid: 'abc' });
103
+ ctx.expect(result.needsSync).toBe(false);
104
+ } finally {
105
+ ctx.omega.auth._firebaseAuth = origAuth;
106
+ }
107
+ },
108
+ },
109
+ {
110
+ name: 'sync-request: main signed out, renderer signed in → tells renderer to sign out',
111
+ run: async (ctx) => {
112
+ const origAuth = ctx.omega.auth._firebaseAuth;
113
+ ctx.omega.auth._firebaseAuth = { currentUser: null };
114
+ try {
115
+ const result = await ctx.omega.ipc.invoke('desktop:auth:sync-request', { contextUid: 'someone' });
116
+ ctx.expect(result.needsSync).toBe(true);
117
+ ctx.expect(result.signOut).toBe(true);
118
+ } finally {
119
+ ctx.omega.auth._firebaseAuth = origAuth;
120
+ }
121
+ },
122
+ },
123
+ {
124
+ name: 'sync-request: firebase not loaded → no sync',
125
+ run: async (ctx) => {
126
+ const origAuth = ctx.omega.auth._firebaseAuth;
127
+ ctx.omega.auth._firebaseAuth = null;
128
+ try {
129
+ const result = await ctx.omega.ipc.invoke('desktop:auth:sync-request', { contextUid: null });
130
+ ctx.expect(result.needsSync).toBe(false);
131
+ ctx.expect(result.reason).toBe('firebase-not-loaded');
132
+ } finally {
133
+ ctx.omega.auth._firebaseAuth = origAuth;
134
+ }
135
+ },
136
+ },
137
+ {
138
+ name: 'sign-out IPC succeeds when nothing is signed in',
139
+ run: async (ctx) => {
140
+ const result = await ctx.omega.ipc.invoke('desktop:auth:sign-out');
141
+ ctx.expect(result.success).toBe(true);
142
+ },
143
+ },
144
+ {
145
+ name: 'get-user IPC answers the signed-out account and no identity when no current user',
146
+ run: async (ctx) => {
147
+ const result = await ctx.omega.ipc.invoke('desktop:auth:get-user');
148
+ ctx.expect(result.uid).toBeNull();
149
+ ctx.expect(result.identity).toBeNull();
150
+ ctx.expect(result.document).toEqual(ctx.omega.auth.user.toJSON());
151
+ ctx.expect(new User(result.document).authenticated).toBe(false);
152
+ },
153
+ },
154
+ {
155
+ // A renderer's `omega-signin` trigger lands here: main's own sign-in flow.
156
+ // openAuthFlow is stubbed because the real one opens the user's browser.
157
+ name: 'open-flow IPC runs omega.openAuthFlow()',
158
+ run: async (ctx) => {
159
+ let flows = 0;
160
+ ctx.omega.openAuthFlow = async () => { flows++; return { url: 'https://localhost:4000/signin' }; };
161
+ try {
162
+ const result = await ctx.omega.ipc.invoke('desktop:auth:open-flow');
163
+ ctx.expect(flows).toBe(1);
164
+ ctx.expect(result).toEqual({ url: 'https://localhost:4000/signin' });
165
+ } finally {
166
+ delete ctx.omega.openAuthFlow;
167
+ }
168
+ },
169
+ },
170
+ {
171
+ // A renderer's `omega-account` trigger: the website's /account page, on the
172
+ // host getWebsiteUrl() answers (the staged dev origin in this testing run)
173
+ name: 'open-account IPC opens <website>/account in the user\'s browser',
174
+ run: async (ctx) => {
175
+ const electron = require('electron');
176
+ const opened = [];
177
+ const origOpen = electron.shell.openExternal;
178
+ const origDev = ctx.omega.config.dev;
179
+ electron.shell.openExternal = async (url) => { opened.push(url); };
180
+ ctx.omega.config.dev = { origin: 'https://localhost:4000' };
181
+ try {
182
+ await ctx.omega.ipc.invoke('desktop:auth:open-account');
183
+ ctx.expect(opened).toEqual(['https://localhost:4000/account']);
184
+ } finally {
185
+ electron.shell.openExternal = origOpen;
186
+ if (origDev !== undefined) ctx.omega.config.dev = origDev; else delete ctx.omega.config.dev;
187
+ }
188
+ },
189
+ },
190
+ {
191
+ name: 'listen: a catch-up with the current state after it returns, then every landing, until unsubscribed',
192
+ run: async (ctx) => {
193
+ const auth = ctx.omega.auth;
194
+ const heard = [];
195
+ const off = auth.listen((state) => heard.push(state.user));
196
+
197
+ // The catch-up is async: nothing is heard before listen() returns
198
+ ctx.expect(heard.length).toBe(0);
199
+ await Promise.resolve();
200
+ ctx.expect(heard.length).toBe(1);
201
+ ctx.expect(heard[0]).toBe(auth.user);
202
+
203
+ const landed = new User({}, { uid: 'u-listen', email: 'l@x.y' });
204
+ auth._land(landed);
205
+ ctx.expect(heard.length).toBe(2);
206
+ ctx.expect(heard[1]).toBe(landed);
207
+
208
+ off();
209
+ auth._land(new User());
210
+ ctx.expect(heard.length).toBe(2);
211
+ ctx.expect(auth.user.authenticated).toBe(false);
212
+ },
213
+ },
214
+ {
215
+ name: '_identity returns the sign-in identity shape (no token)',
216
+ run: (ctx) => {
217
+ const snap = ctx.omega.auth._identity({
218
+ uid: 'u1',
219
+ email: 'a@b.com',
220
+ displayName: 'Bob',
221
+ photoURL: 'http://x/p.png',
222
+ emailVerified: true,
223
+ // sensitive stuff that should NOT be exposed
224
+ stsTokenManager: { accessToken: 'SECRET' },
225
+ providerData: ['xxx'],
226
+ });
227
+ ctx.expect(snap).toEqual({
228
+ uid: 'u1',
229
+ email: 'a@b.com',
230
+ displayName: 'Bob',
231
+ photoURL: 'http://x/p.png',
232
+ emailVerified: true,
233
+ });
234
+ },
235
+ },
236
+ {
237
+ // The emulator gate (#46). _getFirebaseAuth resolves firebase through the
238
+ // auth's OWN seams (_firebase / _firebaseModule, filled by
239
+ // _tryLoadFirebase) — swapping those recording namespaces in drives the real
240
+ // method, no stubbing of the module under test. The omega instance is an input too:
241
+ // isTesting() is the whole gate.
242
+ name: 'auth emulator: a testing run connects, dev/production never do',
243
+ run: (ctx) => {
244
+ const auth = ctx.omega.auth;
245
+ const saved = {
246
+ firebase: auth._firebase,
247
+ module: auth._firebaseModule,
248
+ auth: auth._firebaseAuth,
249
+ omega: auth._omega,
250
+ port: process.env.OMEGA_AUTH_PORT,
251
+ };
252
+
253
+ // One recording firebase namespace pair, reused per run
254
+ const connects = [];
255
+ const authInstance = { __auth: true };
256
+ auth._firebase = {
257
+ getApp: () => { throw new Error('no app'); },
258
+ initializeApp: () => ({ __app: true }),
259
+ };
260
+ auth._firebaseModule = {
261
+ getAuth: () => authInstance,
262
+ connectAuthEmulator: (auth, url, opts) => connects.push({ auth, url, opts }),
263
+ };
264
+
265
+ const run = (isTesting, dev) => {
266
+ auth._firebaseAuth = null;
267
+ auth._omega = {
268
+ config: { cloud: { config: { apiKey: 'AIza-test', projectId: 'demo-desktop' } }, ...(dev ? { dev } : {}) },
269
+ isTesting: () => isTesting,
270
+ };
271
+ return auth._getFirebaseAuth(null);
272
+ };
273
+
274
+ try {
275
+ delete process.env.OMEGA_AUTH_PORT;
276
+
277
+ // production / dev shape — no emulator, ever
278
+ ctx.expect(run(false)).toBe(authInstance);
279
+ ctx.expect(connects.length).toBe(0);
280
+
281
+ // testing shape: the port comes from the map the bundle baked, which
282
+ // carries @omega.js/config's classic 9099 as its floor
283
+ // ([#834](https://github.com/Omega-JS-Stack/omega/issues/834)).
284
+ ctx.expect(run(true, { ports: { auth: 9099 } })).toBe(authInstance);
285
+ ctx.expect(connects.length).toBe(1);
286
+ ctx.expect(connects[0].auth).toBe(authInstance);
287
+ ctx.expect(connects[0].url).toBe('http://localhost:9099');
288
+
289
+ // a bumped port arrives on OMEGA_AUTH_PORT (N7)
290
+ process.env.OMEGA_AUTH_PORT = '9199';
291
+ run(true, { ports: { auth: 9099 } });
292
+ ctx.expect(connects.length).toBe(2);
293
+ ctx.expect(connects[1].url).toBe('http://localhost:9199');
294
+
295
+ // Neither channel is a broken artifact, and it says so by name
296
+ // rather than dialling a port nothing identity-checks (#834).
297
+ delete process.env.OMEGA_AUTH_PORT;
298
+ let threw;
299
+ try { run(true); } catch (e) { threw = e; }
300
+ ctx.expect(threw).toBeDefined();
301
+ ctx.expect(threw.message).toMatch(/dev port for `auth`/);
302
+ ctx.expect(threw.message).toMatch(/bundle task/);
303
+ ctx.expect(connects.length).toBe(2);
304
+ } finally {
305
+ auth._firebase = saved.firebase;
306
+ auth._firebaseModule = saved.module;
307
+ auth._firebaseAuth = saved.auth;
308
+ auth._omega = saved.omega;
309
+ if (saved.port === undefined) delete process.env.OMEGA_AUTH_PORT;
310
+ else process.env.OMEGA_AUTH_PORT = saved.port;
311
+ }
312
+ },
313
+ },
314
+ {
315
+ name: 'deep-link auth/token route is wired to handleToken',
316
+ run: async (ctx) => {
317
+ // We've already tested the deep-link side in deep-link.test.js. Here we verify
318
+ // the integration is actually live: dispatching the route triggers our spy.
319
+ let received = null;
320
+ const orig = ctx.omega.auth.handleToken;
321
+ ctx.omega.auth.handleToken = async (token) => {
322
+ received = token;
323
+ return { success: true };
324
+ };
325
+ try {
326
+ ctx.omega.deepLink.dispatch('myapp://auth/token?authToken=BRIDGE-TEST-TOKEN');
327
+ // Built-in handler is sync up to the await — give it a tick.
328
+ await new Promise((r) => setImmediate(r));
329
+ ctx.expect(received).toBe('BRIDGE-TEST-TOKEN');
330
+ } finally {
331
+ ctx.omega.auth.handleToken = orig;
332
+ }
333
+ },
334
+ },
335
+ ],
336
+ });