Skip to content

Commit ddc28f0

Browse files
committed
PR feedback, use emulators:exec instead
1 parent 17a1679 commit ddc28f0

File tree

11 files changed

+101
-116
lines changed

11 files changed

+101
-116
lines changed

packages-exp/auth-exp/README.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,27 @@ are not broken into such granular details. Check out `package.json` for more.
2525

2626
To test against the emulator, set up the Auth emulator
2727
([instructions](https://firebase.google.com/docs/emulator-suite/connect_and_prototype)).
28-
When starting the emulators, make sure to specify the project ID manually
29-
when issuing the command (the local integration tests use a fake project ID):
30-
`firebase emulators:start --project test-emulator`). Once the emulator is
31-
started and waiting, you can execute the tests. In addition to the npm commands
32-
above, you can execute the following:
28+
The easiest way to run these tests is to use the `firebase emulators:exec`
29+
command
30+
([documentation](https://firebase.google.com/docs/emulator-suite/install_and_configure#startup)).
31+
You can also manually start the emulator separately, and then point the tests
32+
to it by setting the `GCLOUD_PROJECT` and `FIREBASE_AUTH_EMULATOR_HOST`
33+
environmental variables. In addition to the commands listed above, the below
34+
commands also run various tests:
3335

3436
* `yarn test:integration:local` — Executes Node and browser emulator
3537
integration tests, as well as the Selenium WebDriver tests
3638

3739
* `yarn test:webdriver` — Executes only the Selenium WebDriver
3840
integration tests
3941

42+
For example, to run all integration and WebDriver tests against the emulator,
43+
you would simply execute the following command:
44+
45+
```sh
46+
firebase emulators:exec --project foo-bar --only auth "yarn test:integration:local"
47+
```
48+
4049
### Selenium Webdriver tests
4150

4251
These tests assume that you have both Firefox and Chrome installed on your

packages-exp/auth-exp/karma.conf.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,20 @@ function getClientConfig(argv) {
5757
return {};
5858
}
5959

60+
if (!process.env.GCLOUD_PROJECT || !process.env.FIREBASE_AUTH_EMULATOR_HOST) {
61+
console.error('Local testing against emulator requested, but ' +
62+
'GCLOUD_PROJECT and FIREBASE_AUTH_EMULATOR_HOST env variables ' +
63+
'are missing');
64+
process.exit(1);
65+
}
66+
6067
return {
6168
authAppConfig: {
6269
apiKey: 'local-api-key',
63-
projectId: 'test-emulator',
70+
projectId: process.env.GCLOUD_PROJECT,
6471
authDomain: 'local-auth-domain'
6572
},
66-
authEmulatorPort: '9099'
73+
authEmulatorHost: process.env.FIREBASE_AUTH_EMULATOR_HOST,
6774
};
6875
}
6976

packages-exp/auth-exp/scripts/run-node-tests.js

Lines changed: 2 additions & 58 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages-exp/auth-exp/scripts/run-node-tests.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,28 @@ let args = [
6161
'../../config/mocharc.node.js'
6262
];
6363

64+
// Make sure that the environment variables are present for local test
6465
if (argv.local) {
65-
process.env.AUTH_EMULATOR_PORT = '9099';
66-
process.env.AUTH_EMULATOR_PROJECT_ID = 'test-emulator';
66+
if (!process.env.GCLOUD_PROJECT || !process.env.FIREBASE_AUTH_EMULATOR_HOST) {
67+
console.error('Local testing against emulator requested, but ' +
68+
'GCLOUD_PROJECT and FIREBASE_AUTH_EMULATOR_HOST env variables ' +
69+
'are missing');
70+
process.exit(1);
71+
}
6772
}
6873

6974
args = args.concat(argv._ as string[]);
7075

71-
const childProcess = spawn(nyc, args, {
76+
const spawned = spawn(nyc, args, {
7277
stdio: 'inherit',
7378
cwd: process.cwd()
74-
}).childProcess;
79+
});
80+
81+
const childProcess = spawned.childProcess;
82+
spawned.catch(() => {
83+
childProcess.kill();
84+
process.exit(1);
85+
});
7586

7687
process.once('exit', () => childProcess.kill());
7788
process.once('SIGINT', () => childProcess.kill('SIGINT'));

packages-exp/auth-exp/test/helpers/integration/emulator_rest_helpers.ts

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@
1515
* limitations under the License.
1616
*/
1717

18-
// eslint-disable-next-line import/no-extraneous-dependencies
19-
import { Auth } from '@firebase/auth-exp';
20-
import { getApps } from '@firebase/app-exp';
2118
import { FetchProvider } from '../../../src/core/util/fetch_provider';
2219
import * as fetchImpl from 'node-fetch';
20+
import { getAppConfig, getEmulatorUrl } from './settings';
2321

2422
if (typeof document !== 'undefined') {
2523
FetchProvider.initialize(fetch);
@@ -53,10 +51,8 @@ interface OobCodesResponse {
5351
}
5452

5553
export async function getPhoneVerificationCodes(
56-
auth: Auth
5754
): Promise<Record<string, VerificationSession>> {
58-
assertEmulator(auth);
59-
const url = getEmulatorUrl(auth, 'verificationCodes');
55+
const url = buildEmulatorUrlForPath('verificationCodes');
6056
const response: VerificationCodesResponse = await (
6157
await FetchProvider.fetch()(url)
6258
).json();
@@ -67,27 +63,21 @@ export async function getPhoneVerificationCodes(
6763
}, {} as Record<string, VerificationSession>);
6864
}
6965

70-
export async function getOobCodes(auth: Auth): Promise<OobCodeSession[]> {
71-
assertEmulator(auth);
72-
const url = getEmulatorUrl(auth, 'oobCodes');
66+
export async function getOobCodes(): Promise<OobCodeSession[]> {
67+
const url = buildEmulatorUrlForPath('oobCodes');
7368
const response: OobCodesResponse = await (
7469
await FetchProvider.fetch()(url)
7570
).json();
7671
return response.oobCodes;
7772
}
7873

79-
function getEmulatorUrl(auth: Auth, endpoint: string): string {
80-
const { host, port, protocol } = auth.emulatorConfig!;
81-
const projectId = getProjectId(auth);
82-
return `${protocol}://${host}:${port}/emulator/v1/projects/${projectId}/${endpoint}`;
74+
export async function resetEmulator(): Promise<void> {
75+
const url = buildEmulatorUrlForPath('accounts');
76+
await FetchProvider.fetch()(url, {method: 'DELETE'});
8377
}
8478

85-
function getProjectId(auth: Auth): string {
86-
return getApps().find(app => app.name === auth.name)!.options.projectId!;
87-
}
88-
89-
function assertEmulator(auth: Auth): void {
90-
if (!auth.emulatorConfig) {
91-
throw new Error("Can't fetch OOB codes against prod API");
92-
}
79+
function buildEmulatorUrlForPath(endpoint: string): string {
80+
const emulatorBaseUrl = getEmulatorUrl();
81+
const projectId = getAppConfig().projectId;
82+
return `${emulatorBaseUrl}/emulator/v1/projects/${projectId}/${endpoint}`;
9383
}

packages-exp/auth-exp/test/helpers/integration/helpers.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Auth, User } from '../../../src/model/public_types';
2222
import { getAuth, useAuthEmulator } from '../../../'; // Use browser OR node dist entrypoint depending on test env.
2323
import { _generateEventId } from '../../../src/core/util/event_id';
2424
import { getAppConfig, getEmulatorUrl } from './settings';
25+
import { resetEmulator } from './emulator_rest_helpers';
2526

2627
interface IntegrationTestAuth extends Auth {
2728
cleanUp(): Promise<void>;
@@ -55,12 +56,17 @@ export function getTestInstance(requireEmulator = false): Auth {
5556
});
5657

5758
auth.cleanUp = async () => {
58-
// Clear out any new users that were created in the course of the test
59-
for (const user of createdUsers) {
60-
try {
61-
await user.delete();
62-
} catch {
63-
// Best effort. Maybe the test already deleted the user ¯\_(ツ)_/¯
59+
// If we're in an emulated environment, the emulator will clean up for us
60+
if (emulatorUrl) {
61+
await resetEmulator();
62+
} else {
63+
// Clear out any new users that were created in the course of the test
64+
for (const user of createdUsers) {
65+
try {
66+
await user.delete();
67+
} catch {
68+
// Best effort. Maybe the test already deleted the user ¯\_(ツ)_/¯
69+
}
6470
}
6571
}
6672

packages-exp/auth-exp/test/helpers/integration/settings.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ declare const __karma__: any;
2424
// eslint-disable-next-line @typescript-eslint/no-require-imports
2525
const PROJECT_CONFIG = require('../../../../../config/project.json');
2626

27-
const EMULATOR_PORT = process.env.AUTH_EMULATOR_PORT;
28-
const EMULATOR_PROJECT_ID = process.env.AUTH_EMULATOR_PROJECT_ID;
27+
const EMULATOR_HOST = process.env.FIREBASE_AUTH_EMULATOR_HOST;
28+
const EMULATOR_PROJECT_ID = process.env.GCLOUD_PROJECT;
2929

30-
export const USE_EMULATOR = !!EMULATOR_PORT;
30+
export const USE_EMULATOR = !!EMULATOR_HOST;
3131

3232
export const PROJECT_ID = USE_EMULATOR
3333
? EMULATOR_PROJECT_ID
@@ -52,11 +52,8 @@ export function getAppConfig(): FirebaseOptions {
5252

5353
export function getEmulatorUrl(): string | null {
5454
// Check karma first, then fallback on node process
55-
const emulatorPort: string | null =
56-
getKarma()?.config?.authEmulatorPort ||
57-
(USE_EMULATOR ? EMULATOR_PORT : null);
58-
59-
return emulatorPort ? `http://localhost:${emulatorPort}` : null;
55+
const host = getKarma()?.config?.authEmulatorHost || (USE_EMULATOR ? EMULATOR_HOST : null);
56+
return host ? `http://${host}` : null;
6057
}
6158

6259
// eslint-disable-next-line @typescript-eslint/no-explicit-any

packages-exp/auth-exp/test/integration/flows/oob.local.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe('Integration test: oob codes', () => {
7777
});
7878

7979
async function code(toEmail: string): Promise<OobCodeSession> {
80-
const codes = await getOobCodes(auth);
80+
const codes = await getOobCodes();
8181
return codes.reverse().find(({ email }) => email === toEmail)!;
8282
}
8383

packages-exp/auth-exp/test/integration/flows/phone.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ describe('Integration test: phone auth', () => {
8888
fallback: string
8989
): Promise<string> {
9090
if (auth.emulatorConfig) {
91-
const codes = await getPhoneVerificationCodes(auth);
91+
const codes = await getPhoneVerificationCodes();
9292
const vid = typeof crOrId === 'string' ? crOrId : crOrId.verificationId;
9393
return codes[vid].code;
9494
}

packages-exp/auth-exp/test/integration/webdriver/static/index.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,11 @@ window.userSnap = async () => auth.currentUser;
4242

4343
window.authSnap = async () => auth;
4444

45-
// Initialize library and attach globals
46-
(async () => {
47-
const app = initializeApp({
48-
apiKey: 'api-key',
49-
projectId: 'test-emulator',
50-
authDomain: 'http://localhost/emulator'
51-
});
45+
// The config and emulator URL are injected by the test. The test framework
46+
// calls this function after that injection.
47+
window.startAuth = async () => {
48+
const app = initializeApp(firebaseConfig);
5249
auth = getAuth(app);
53-
useAuthEmulator(auth, 'http://localhost:9099');
50+
useAuthEmulator(auth, emulatorUrl);
5451
window.auth = auth;
55-
})();
52+
}

packages-exp/auth-exp/test/integration/webdriver/util/authdriver.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
// eslint-disable-next-line import/no-extraneous-dependencies
1919
import { Auth, User } from '@firebase/auth-exp';
2020
import { Builder, WebDriver } from 'selenium-webdriver';
21+
import { resetEmulator } from '../../../helpers/integration/emulator_rest_helpers';
22+
import { getEmulatorUrl, PROJECT_ID, USE_EMULATOR } from '../../../helpers/integration/settings';
2123
import { authTestServer } from './test_server';
2224

2325
/** Browsers to run the tests on */
@@ -29,7 +31,8 @@ export enum TestFunction {
2931
RESET = 'reset',
3032
AWAIT_AUTH_INIT = 'authInit',
3133
USER_SNAPSHOT = 'userSnap',
32-
AUTH_SNAPSHOT = 'authSnap'
34+
AUTH_SNAPSHOT = 'authSnap',
35+
START_AUTH = 'startAuth',
3336
}
3437

3538
/** Helper wraper around the WebDriver object */
@@ -40,6 +43,7 @@ export class AuthDriver {
4043
await authTestServer.start();
4144
this.webDriver = await new Builder().forBrowser(browser).build();
4245
await this.webDriver.get(authTestServer.address!);
46+
await this.injectConfigAndInitAuth();
4347
}
4448

4549
async stop(): Promise<void> {
@@ -92,6 +96,7 @@ export class AuthDriver {
9296
}
9397

9498
async reset(): Promise<void> {
99+
await resetEmulator();
95100
return this.call(TestFunction.RESET);
96101
}
97102

@@ -101,6 +106,25 @@ export class AuthDriver {
101106

102107
async refresh(): Promise<void> {
103108
await this.webDriver.navigate().refresh();
109+
await this.injectConfigAndInitAuth();
104110
await this.waitForAuthInit();
105111
}
112+
113+
private async injectConfigAndInitAuth(): Promise<void> {
114+
if (!USE_EMULATOR) {
115+
throw new Error('Local testing against emulator requested, but ' +
116+
'GCLOUD_PROJECT and FIREBASE_AUTH_EMULATOR_HOST env variables ' +
117+
'are missing');
118+
}
119+
120+
await this.webDriver.executeScript(`
121+
window.firebaseConfig = {
122+
apiKey: 'emulator-api-key',
123+
projectId: '${PROJECT_ID}',
124+
authDomain: 'http://localhost/emulator',
125+
};
126+
window.emulatorUrl = '${getEmulatorUrl()}';
127+
`);
128+
await this.call(TestFunction.START_AUTH);
129+
}
106130
}

0 commit comments

Comments
 (0)