Skip to content

Commit 712dfde

Browse files
zwu52Kai Wu
and
Kai Wu
authored
Enable FCM Integration Test (firebase#3145)
* 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 firebase#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 Co-authored-by: Kai Wu <[email protected]>
1 parent e1df44f commit 712dfde

39 files changed

+725
-834
lines changed

integration/messaging/download-browsers.js

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @license
3-
* Copyright 2017 Google Inc.
3+
* Copyright 2017 Google LLC
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -18,14 +18,9 @@
1818
const seleniumAssistant = require('selenium-assistant');
1919

2020
console.log('Starting browser download - this may take some time.');
21-
Promise.all([
22-
seleniumAssistant.downloadLocalBrowser('chrome', 'stable', 48),
23-
seleniumAssistant.downloadLocalBrowser('chrome', 'beta', 48),
24-
seleniumAssistant.downloadLocalBrowser('chrome', 'unstable', 48),
25-
seleniumAssistant.downloadLocalBrowser('firefox', 'stable', 48),
26-
seleniumAssistant.downloadLocalBrowser('firefox', 'beta', 48),
27-
seleniumAssistant.downloadLocalBrowser('firefox', 'unstable', 48)
28-
])
21+
// TODO: enable firefox testing once figure out how to give notification permission with SE webdriver.
22+
// TODO: Run the integration test against multiple major chrome versions to ensure backward compatibility
23+
Promise.all([seleniumAssistant.downloadLocalBrowser('chrome', 'stable', 80)])
2924
.then(() => {
3025
console.log('Browser download complete.');
3126
})

integration/messaging/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
"version": "0.2.1",
55
"scripts": {
66
"pretest:manual": "node ./download-browsers.js",
7-
"test": "echo 'Tests disabled due to flakiness'",
7+
"test": "mocha --exit",
88
"test:manual": "mocha --exit"
99
},
1010
"dependencies": {
1111
"firebase": "7.15.1"
1212
},
1313
"devDependencies": {
1414
"chai": "4.2.0",
15-
"chromedriver": "80.0.2",
15+
"chromedriver": "^83.0.0",
1616
"express": "4.17.1",
1717
"geckodriver": "1.19.1",
1818
"mocha": "7.1.2",

integration/messaging/test/static/app.js

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @license
3-
* Copyright 2017 Google Inc.
3+
* Copyright 2017 Google LLC
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -15,8 +15,6 @@
1515
* limitations under the License.
1616
*/
1717

18-
const EIGHT_DAYS_IN_MS = 8 * 86400000;
19-
2018
/**
2119
* This class just wraps the expected behavior or a demo app that will
2220
* be orchestrated by selenium tests (Although manual use of the demo will
@@ -36,40 +34,49 @@ class DemoApp {
3634
this._triggerDeleteToken = this.triggerDeleteToken;
3735
this._triggerGetToken = this.triggerGetToken;
3836
this._triggerTimeForward = this.triggerTimeForward;
37+
this._clearInstanceForTest = this.clearInstanceForTest;
38+
this.appendMessage = this.appendMessage;
3939

4040
// Initialize Firebase
4141
firebase.initializeApp(firebaseConfig);
4242

4343
this._messaging = firebase.messaging();
4444

45-
if (options.applicationKey) {
46-
this._messaging.usePublicVapidKey(options.applicationKey);
45+
if (options.vapidKey) {
46+
console.debug('VapidKey is provided to the test app. ');
47+
this._messaging.usePublicVapidKey(options.vapidKey);
48+
} else {
49+
console.debug('VapidKey is not specified. Skip setting it');
4750
}
4851

4952
if (options.swReg) {
53+
console.debug('ServiceWorker is provided to the test app');
5054
this._messaging.useServiceWorker(options.swReg);
55+
} else {
56+
console.debug(
57+
'ServiceWorker is not specified. The default ServiceWorker will be used.'
58+
);
5159
}
5260

53-
this._messaging.onMessage(payload => {
54-
console.log(`Message received: `, payload);
55-
this._messages.push(payload);
61+
this._messaging.onMessage(message => {
62+
this.appendMessage(message);
5663
});
5764

58-
// Initializa state of token
59-
this._messaging
60-
.requestPermission()
61-
.then(() => this._messaging.getToken())
62-
.then(
63-
token => {
64-
console.log('getToken() worked: ', token);
65-
this._token = token;
66-
},
67-
err => {
68-
console.log('getToken() failed: ', err.message, err.stack);
69-
this._errors.push(err);
70-
this._token = null;
71-
}
72-
);
65+
this._messaging.getToken().then(
66+
token => {
67+
console.log('Test app getToken() succeed. Token: ', token);
68+
this._token = token;
69+
},
70+
err => {
71+
console.log('Test app getToken() failed: ', err.message, err.stack);
72+
this._errors.push(err);
73+
this._token = null;
74+
}
75+
);
76+
}
77+
78+
appendMessage(payload) {
79+
this._messages.push(payload);
7380
}
7481

7582
async triggerDeleteToken(token) {
@@ -95,10 +102,15 @@ class DemoApp {
95102
return this._token;
96103
}
97104

98-
async triggerTimeForward() {
105+
triggerTimeForward() {
99106
this._clock.tick(EIGHT_DAYS_IN_MS);
100107
}
101108

109+
clearInstanceForTest() {
110+
this._errors = [];
111+
this._messages = [];
112+
}
113+
102114
get token() {
103115
return this._token;
104116
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
// Test project managed by the FCM team
19+
const PUBLIC_VAPID_KEY =
20+
'BNjjus3nz38aYtbDLVfunY3VULg0Yq5T4GXWd7iDDmeqWCUNqfrK1eiKVdoT0VncLuCjfJ1GmdfmNZz-AjHfkxM';
21+
const FIREBASE_CONFIG = {
22+
apiKey: 'AIzaSyBpIe0xyUNHOwtE_go32NmUJF4acsc6S6c',
23+
projectId: 'fcm-web-sdk-test',
24+
messagingSenderId: '750970317741',
25+
appId: '1:750970317741:web:f382be3155e250906a4f24'
26+
};
27+
28+
const TAG = 'FCM_INTEGRATION_TEST: ';
29+
const EIGHT_DAYS_IN_MS = 8 * 86400000;
30+
const TEST_DB = 'FCM_INTEGRATION_TEST_DB';
31+
const TEST_DB_VERSION = 1;
32+
const BACKGROUND_MESSAGES_OBJECT_STORE = 'background_messages';
33+
// indexDb object store creation require a "primary key", "ndx" is used.
34+
const BACKGROUND_MESSAGES_OBJECT_STORE_PRIMARY_KEY = 'ndx';
35+
const BACKGROUND_MESSAGES_OBJECT_STORE_DEFAULT_NDX = 'default_ndx';
Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
<html>
2-
<head>
3-
<title>FCM Demo</title>
4-
<meta name="viewport" content="width=device-width,initial-scale=1">
5-
</head>
6-
<body>
7-
<h1>Default SW</h1>
8-
<script src="/firebase/firebase-app.js"></script>
9-
<script src="/firebase/firebase-messaging.js"></script>
10-
<script src="/app.js"></script>
11-
<script src="../valid-no-vapid-key/firebaseConfig.js"></script>
12-
<script src="https://cdnjs.cloudflare.com/ajax/libs/sinon.js/4.1.3/sinon.min.js"></script>
13-
<script>
14-
window.__test = new window.DemoApp(window.firebaseConfig);
15-
</script>
16-
</body>
17-
</html>
2+
<head>
3+
<title>FCM Demo</title>
4+
<meta name="viewport" content="width=device-width,initial-scale=1" />
5+
</head>
6+
<body>
7+
<h1>Default SW</h1>
8+
<script src="/firebase-app.js"></script>
9+
<script src="/firebase-messaging.js"></script>
10+
<script src="../app.js"></script>
11+
<script src="../constants.js"></script>
12+
<script src="https://cdnjs.cloudflare.com/ajax/libs/sinon.js/4.1.3/sinon.min.js"></script>
13+
<script>
14+
window.__test = new window.DemoApp(FIREBASE_CONFIG, {
15+
vapidKey: PUBLIC_VAPID_KEY
16+
});
17+
</script>
18+
</body>
19+
</html>
Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @license
3-
* Copyright 2017 Google Inc.
3+
* Copyright 2017 Google LLC
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -15,13 +15,4 @@
1515
* limitations under the License.
1616
*/
1717

18-
importScripts('/sw-shared.js');
19-
importScripts('/valid-no-vapid-key/firebaseConfig.js');
20-
21-
firebase.initializeApp(self.firebaseConfig);
22-
23-
const messaging = firebase.messaging();
24-
messaging.setBackgroundMessageHandler(data => {
25-
const title = 'Background Notification';
26-
return self.registration.showNotification(title, {});
27-
});
18+
importScripts('./sw-base.js');

integration/messaging/test/static/invalid-manifest/index.html

Lines changed: 0 additions & 18 deletions
This file was deleted.

integration/messaging/test/static/invalid-manifest/manifest.json

Lines changed: 0 additions & 4 deletions
This file was deleted.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* @license
3+
* Copyright 2017 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
importScripts('../constants.js');
19+
20+
// HEAD targets served through express
21+
importScripts('/firebase-app.js');
22+
importScripts('/firebase-messaging.js');
23+
24+
firebase.initializeApp(FIREBASE_CONFIG);
25+
const messaging = firebase.messaging();
26+
27+
messaging.setBackgroundMessageHandler(payload => {
28+
console.log(
29+
TAG +
30+
'a background message is received: ' +
31+
JSON.stringify(payload) +
32+
'. Storing it into idb for tests to read...'
33+
);
34+
35+
addPayloadToDb(payload);
36+
});
37+
38+
async function addPayloadToDb(payload) {
39+
const dbOpenReq = indexedDB.open(TEST_DB);
40+
41+
dbOpenReq.onupgradeneeded = () => {
42+
const db = dbOpenReq.result;
43+
44+
// store creation is a synchronized call
45+
console.log('creating object store...');
46+
db.createObjectStore(BACKGROUND_MESSAGES_OBJECT_STORE, {
47+
keyPath: BACKGROUND_MESSAGES_OBJECT_STORE_PRIMARY_KEY
48+
});
49+
};
50+
51+
dbOpenReq.onsuccess = () => {
52+
const db = dbOpenReq.result;
53+
54+
addPayloadToDbInternal(db, {
55+
...payload,
56+
// ndx is required as the primary key of the store. It doesn't have any other testing purpose
57+
ndx: BACKGROUND_MESSAGES_OBJECT_STORE_DEFAULT_NDX
58+
});
59+
};
60+
}
61+
62+
async function addPayloadToDbInternal(db, payload) {
63+
// onsuccess might race with onupgradeneeded. Consequently causing " object stores was not found" error. Therefore, wait briefly for db.createObjectStore to complete
64+
const delay = ms => new Promise(res => setTimeout(res, ms));
65+
await delay(/* milliseconds= */ 30000);
66+
67+
tx = db.transaction(BACKGROUND_MESSAGES_OBJECT_STORE, 'readwrite');
68+
69+
console.log('adding message payload to db: ' + JSON.stringify(payload));
70+
addReq = tx.objectStore(BACKGROUND_MESSAGES_OBJECT_STORE).add(payload);
71+
}
Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
<html>
2-
<head>
3-
<title>FCM Demo</title>
4-
<link rel="manifest" href="./manifest.json">
5-
<meta name="viewport" content="width=device-width,initial-scale=1">
6-
</head>
7-
<body>
8-
<h1>Valid Manifest</h1>
9-
<script src="/firebase/firebase-app.js"></script>
10-
<script src="/firebase/firebase-messaging.js"></script>
11-
<script src="/app.js"></script>
12-
<script src="../valid-no-vapid-key/firebaseConfig.js"></script>
13-
<script src="https://cdnjs.cloudflare.com/ajax/libs/sinon.js/4.1.3/sinon.min.js"></script>
14-
<script>
15-
window.__test = new window.DemoApp(window.firebaseConfig);
16-
</script>
17-
</body>
18-
</html>
2+
<head>
3+
<title>FCM Demo</title>
4+
<link rel="manifest" href="./manifest.json" />
5+
<meta name="viewport" content="width=device-width,initial-scale=1" />
6+
</head>
7+
<body>
8+
<h1>Valid Manifest</h1>
9+
<script src="/firebase-app.js"></script>
10+
<script src="/firebase-messaging.js"></script>
11+
<script src="../app.js"></script>
12+
<script src="../constants.js"></script>
13+
<script src="https://cdnjs.cloudflare.com/ajax/libs/sinon.js/4.1.3/sinon.min.js"></script>
14+
<script>
15+
window.__test = new window.DemoApp(FIREBASE_CONFIG, {});
16+
</script>
17+
</body>
18+
</html>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"gcm_sender_id": "103953800507"
2+
"gcm_sender_id": "750970317741"
33
}

0 commit comments

Comments
 (0)