Skip to content

Commit 469c8bd

Browse files
zwu52Kai Wu
andauthored
Fix FCM rxjs Incompatibility (#3221)
* Enable the FCM integration test (IT) for Chrome. Context: the tests were switched off a couple of years ago due to flakiness. It has not been maintained since then. During the time, Some Selenium API methods used were deprecated and removed; the Firebase projects used are no longer managed or owned by the FCM team. Consequently, the test became unfunctional. In the effort of providing safety for the upcoming FCM releases, this PR is created to fix, deflake, refactor and improve the old tests. This PR did the following: - Enabled comprehensive IT for chrome (ver.80). Now we are covering send&foreground recevie for FCM messages (messages with {notification} payload, {data} payload and {notification, data} payload), delete/update token, use default/customized ServiceWorker. - Defalaked test. The IT is now reasonably stable without retry. Previously we are retrying 1 or 3 times. - Optimized test. Previously we create a webDriver for each test, which is slow and annoying. Because a window is created and brought to focus and killed frequently, it makes working on other tasks and testing nearly impossible (Probably using a headless browser would work but I haven't found a satisfying solution to have the app in the state of foreground and background which is a requirement for FCM functions). With the way the tests are organized, the IT only spin up a new web driver when necessary. Some data on performance: (old) 'test-send' take 74 seconds (only measured 'test-send' because the other test suites were not functional at the time); (now) 'test-send', 'test-deleteToken', 'test-updateToken', 'test-useDefaultServiceWorker', 'test-useValidManifest' takes in total 33s (10 run average). - General refactoring. Including refactors on expect blocks, createWebDriver, use const for constants usage, etc. The code should be much easier to understand and maintain. Future work: - Enable test on firefox once I get the notification permission working. - Run the IC against some milestone chrome/firefox version (if it makes sense) to ensure backward compatibility. We should try to avoid #2712 . :) * [AUTOMATED]: License Headers * Enable integration test (IT) for FCM. Context: FCM IT were turned off a couple years ago due to flakiness. It became mostly unfunctional as repo structure change overtime. The goal is to fix and enable IT for more confident developement flow and safer releases. This CL does the following: - Fix the IT to be functional again. The IT is derteminated from my experiements (no longer flaky). Therefore, The CL removed the retry mechasim (previously retry 3 times) which makes running IT cheaper and more enjoyable. - Include IT for test:change for FCM package: the entire IT test suites is resoanblly fast (from my exeperience 1-3 miutes to complete. As it grows larger, maybe it makes run tests in parallel in Saucelab) Futhure work: - Enable testing for firefox * Correct int syntax js doesn't allow underscore for int * This file wasn't auto-saved * Trigger FCM IT why dot.env(FCM_SECRETS) are not included in env? * Test Secrets can be accessed w/o dotenv * Add fcm sercret to workflow * Update test-changed.yml * test send (background only) Because headless chrome doesn't have the concept of foreground and background * remove dotenv * feed secrest into test:all workflow * Update test-all.yml * background messaging checking * [AUTOMATED]: License Headers * rerun * added waiting * wait * Update test-send.js * Update test-send.js * Examine wrong sercret * Update sendMessage.js * Update sendMessage.js * Update sendMessage.js * Update sendMessage.js * Update sendMessage.js * update fcm project * Update package.json * Update test-send.js * open new tab for backgournd receive * removed test-send somehow not workingin github workflow? * Adding Reties * Change timeout limit * retry 3 times * adjust mocha setting * update * Enable foreground tesing * Fix FCM rxjs incompatibility * Use one listener to handle next and observer for onMessage * Add changeset to the PR * Update tough-rings-bake.md Co-authored-by: Kai Wu <[email protected]>
1 parent ee1892d commit 469c8bd

File tree

3 files changed

+46
-36
lines changed

3 files changed

+46
-36
lines changed

.changeset/tough-rings-bake.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'firebase': patch
3+
'@firebase/messaging': patch
4+
---
5+
6+
Added support for `onMessage` so the internal callback can work with [Subscriber](https://rxjs.dev/api/index/class/Subscriber)

packages/messaging/src/controllers/window-controller.test.ts

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
import '../testing/setup';
2+
3+
import * as tokenManagementModule from '../core/token-management';
4+
5+
import {
6+
CONSOLE_CAMPAIGN_ANALYTICS_ENABLED,
7+
CONSOLE_CAMPAIGN_ID,
8+
CONSOLE_CAMPAIGN_NAME,
9+
CONSOLE_CAMPAIGN_TIME,
10+
DEFAULT_SW_PATH,
11+
DEFAULT_SW_SCOPE,
12+
DEFAULT_VAPID_KEY
13+
} from '../util/constants';
14+
import { InternalMessage, MessageType } from '../interfaces/internal-message';
15+
import { SinonFakeTimers, SinonSpy, spy, stub, useFakeTimers } from 'sinon';
16+
import { Spy, Stub } from '../testing/sinon-types';
17+
18+
import { ErrorCode } from '../util/errors';
19+
import { FakeServiceWorkerRegistration } from '../testing/fakes/service-worker';
20+
import { FirebaseAnalyticsInternal } from '@firebase/analytics-interop-types';
21+
import { FirebaseInternalDependencies } from '../interfaces/internal-dependencies';
22+
import { WindowController } from './window-controller';
123
/**
224
* @license
325
* Copyright 2017 Google LLC
@@ -15,27 +37,7 @@
1537
* limitations under the License.
1638
*/
1739
import { expect } from 'chai';
18-
import { stub, spy, SinonSpy, useFakeTimers, SinonFakeTimers } from 'sinon';
19-
20-
import { FirebaseAnalyticsInternal } from '@firebase/analytics-interop-types';
21-
import { WindowController } from './window-controller';
2240
import { getFakeFirebaseDependencies } from '../testing/fakes/firebase-dependencies';
23-
import { ErrorCode } from '../util/errors';
24-
import { FirebaseInternalDependencies } from '../interfaces/internal-dependencies';
25-
import * as tokenManagementModule from '../core/token-management';
26-
import {
27-
DEFAULT_VAPID_KEY,
28-
DEFAULT_SW_SCOPE,
29-
DEFAULT_SW_PATH,
30-
CONSOLE_CAMPAIGN_ANALYTICS_ENABLED,
31-
CONSOLE_CAMPAIGN_ID,
32-
CONSOLE_CAMPAIGN_NAME,
33-
CONSOLE_CAMPAIGN_TIME
34-
} from '../util/constants';
35-
import { Stub, Spy } from '../testing/sinon-types';
36-
import '../testing/setup';
37-
import { FakeServiceWorkerRegistration } from '../testing/fakes/service-worker';
38-
import { MessageType, InternalMessage } from '../interfaces/internal-message';
3941

4042
type MessageEventListener = (event: Event) => Promise<void>;
4143

packages/messaging/src/controllers/window-controller.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,31 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { getToken, deleteToken } from '../core/token-management';
19-
import { FirebaseInternalDependencies } from '../interfaces/internal-dependencies';
20-
import { FirebaseMessaging } from '@firebase/messaging-types';
21-
import { ERROR_FACTORY, ErrorCode } from '../util/errors';
22-
import { NextFn, Observer, Unsubscribe } from '@firebase/util';
23-
import { InternalMessage, MessageType } from '../interfaces/internal-message';
2418
import {
25-
CONSOLE_CAMPAIGN_ID,
2619
CONSOLE_CAMPAIGN_ANALYTICS_ENABLED,
20+
CONSOLE_CAMPAIGN_ID,
2721
CONSOLE_CAMPAIGN_NAME,
2822
CONSOLE_CAMPAIGN_TIME,
2923
DEFAULT_SW_PATH,
3024
DEFAULT_SW_SCOPE,
3125
DEFAULT_VAPID_KEY
3226
} from '../util/constants';
33-
import { FirebaseApp } from '@firebase/app-types';
27+
import { ERROR_FACTORY, ErrorCode } from '../util/errors';
28+
import { InternalMessage, MessageType } from '../interfaces/internal-message';
29+
import { NextFn, Observer, Unsubscribe } from '@firebase/util';
30+
import { deleteToken, getToken } from '../core/token-management';
31+
3432
import { ConsoleMessageData } from '../interfaces/message-payload';
35-
import { isConsoleMessage } from '../helpers/is-console-message';
33+
import { FirebaseApp } from '@firebase/app-types';
34+
import { FirebaseInternalDependencies } from '../interfaces/internal-dependencies';
35+
import { FirebaseMessaging } from '@firebase/messaging-types';
3636
import { FirebaseService } from '@firebase/app-types/private';
37+
import { isConsoleMessage } from '../helpers/is-console-message';
3738

3839
export class WindowController implements FirebaseMessaging, FirebaseService {
3940
private vapidKey: string | null = null;
4041
private swRegistration?: ServiceWorkerRegistration;
41-
private onMessageCallback: NextFn<object> | null = null;
42+
private onMessageCallback: NextFn<object> | Observer<object> | null = null;
4243

4344
constructor(
4445
private readonly firebaseDependencies: FirebaseInternalDependencies
@@ -131,12 +132,8 @@ export class WindowController implements FirebaseMessaging, FirebaseService {
131132
* message.
132133
* @return The unsubscribe function for the observer.
133134
*/
134-
// TODO: Simplify this to only accept a function and not an Observer.
135135
onMessage(nextOrObserver: NextFn<object> | Observer<object>): Unsubscribe {
136-
this.onMessageCallback =
137-
typeof nextOrObserver === 'function'
138-
? nextOrObserver
139-
: nextOrObserver.next;
136+
this.onMessageCallback = nextOrObserver;
140137

141138
return () => {
142139
this.onMessageCallback = null;
@@ -193,8 +190,13 @@ export class WindowController implements FirebaseMessaging, FirebaseService {
193190

194191
const { type, payload } = (event.data as InternalMessage).firebaseMessaging;
195192

193+
// onMessageCallback is either a function or observer/subscriber.
196194
if (this.onMessageCallback && type === MessageType.PUSH_RECEIVED) {
197-
this.onMessageCallback(payload);
195+
if (typeof this.onMessageCallback === 'function') {
196+
this.onMessageCallback(payload);
197+
} else {
198+
this.onMessageCallback.next(payload);
199+
}
198200
}
199201

200202
const { data } = payload;

0 commit comments

Comments
 (0)