@firebase/rules-unit-testing 3.0.4 → 4.0.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.
package/dist/index.cjs.js CHANGED
@@ -5,78 +5,12 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  require('firebase/compat/database');
6
6
  require('firebase/compat/firestore');
7
7
  require('firebase/compat/storage');
8
- var fetch = require('node-fetch');
9
8
  var firebase = require('firebase/compat/app');
10
9
 
11
10
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
11
 
13
- var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
14
12
  var firebase__default = /*#__PURE__*/_interopDefaultLegacy(firebase);
15
13
 
16
- /******************************************************************************
17
- Copyright (c) Microsoft Corporation.
18
-
19
- Permission to use, copy, modify, and/or distribute this software for any
20
- purpose with or without fee is hereby granted.
21
-
22
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
23
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
24
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
25
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
26
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
27
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
28
- PERFORMANCE OF THIS SOFTWARE.
29
- ***************************************************************************** */
30
-
31
- var __assign = function() {
32
- __assign = Object.assign || function __assign(t) {
33
- for (var s, i = 1, n = arguments.length; i < n; i++) {
34
- s = arguments[i];
35
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
36
- }
37
- return t;
38
- };
39
- return __assign.apply(this, arguments);
40
- };
41
-
42
- function __awaiter(thisArg, _arguments, P, generator) {
43
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
44
- return new (P || (P = Promise))(function (resolve, reject) {
45
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
46
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
47
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
48
- step((generator = generator.apply(thisArg, _arguments || [])).next());
49
- });
50
- }
51
-
52
- function __generator(thisArg, body) {
53
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
54
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
55
- function verb(n) { return function (v) { return step([n, v]); }; }
56
- function step(op) {
57
- if (f) throw new TypeError("Generator is already executing.");
58
- while (_) try {
59
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
60
- if (y = 0, t) op = [op[0] & 2, t.value];
61
- switch (op[0]) {
62
- case 0: case 1: t = op; break;
63
- case 4: _.label++; return { value: op[1], done: false };
64
- case 5: _.label++; y = op[1]; op = [0]; continue;
65
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
66
- default:
67
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
68
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
69
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
70
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
71
- if (t[2]) _.ops.pop();
72
- _.trys.pop(); continue;
73
- }
74
- op = body.call(thisArg, _);
75
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
76
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
77
- }
78
- }
79
-
80
14
  /**
81
15
  * @license
82
16
  * Copyright 2021 Google LLC
@@ -118,15 +52,15 @@ function fixHostname(host, fallbackHost) {
118
52
  */
119
53
  function makeUrl(hostAndPort, path) {
120
54
  if (typeof hostAndPort === 'object') {
121
- var host = hostAndPort.host, port = hostAndPort.port;
55
+ const { host, port } = hostAndPort;
122
56
  if (host.includes(':')) {
123
- hostAndPort = "[".concat(host, "]:").concat(port);
57
+ hostAndPort = `[${host}]:${port}`;
124
58
  }
125
59
  else {
126
- hostAndPort = "".concat(host, ":").concat(port);
60
+ hostAndPort = `${host}:${port}`;
127
61
  }
128
62
  }
129
- var url = new URL("http://".concat(hostAndPort, "/"));
63
+ const url = new URL(`http://${hostAndPort}/`);
130
64
  url.pathname = path;
131
65
  return url;
132
66
  }
@@ -153,50 +87,38 @@ function makeUrl(hostAndPort, path) {
153
87
  * @param hub the host and port where the Emulator Hub is running
154
88
  * @private
155
89
  */
156
- function discoverEmulators(hub, fetch) {
157
- if (fetch === void 0) { fetch = fetch__default["default"]; }
158
- return __awaiter(this, void 0, void 0, function () {
159
- var res, emulators, data;
160
- return __generator(this, function (_a) {
161
- switch (_a.label) {
162
- case 0: return [4 /*yield*/, fetch(makeUrl(hub, '/emulators'))];
163
- case 1:
164
- res = _a.sent();
165
- if (!res.ok) {
166
- throw new Error("HTTP Error ".concat(res.status, " when attempting to reach Emulator Hub at ").concat(res.url, ", are you sure it is running?"));
167
- }
168
- emulators = {};
169
- return [4 /*yield*/, res.json()];
170
- case 2:
171
- data = _a.sent();
172
- if (data.database) {
173
- emulators.database = {
174
- host: data.database.host,
175
- port: data.database.port
176
- };
177
- }
178
- if (data.firestore) {
179
- emulators.firestore = {
180
- host: data.firestore.host,
181
- port: data.firestore.port
182
- };
183
- }
184
- if (data.storage) {
185
- emulators.storage = {
186
- host: data.storage.host,
187
- port: data.storage.port
188
- };
189
- }
190
- if (data.hub) {
191
- emulators.hub = {
192
- host: data.hub.host,
193
- port: data.hub.port
194
- };
195
- }
196
- return [2 /*return*/, emulators];
197
- }
198
- });
199
- });
90
+ async function discoverEmulators(hub, fetchImpl = fetch) {
91
+ const res = await fetchImpl(makeUrl(hub, '/emulators'));
92
+ if (!res.ok) {
93
+ throw new Error(`HTTP Error ${res.status} when attempting to reach Emulator Hub at ${res.url}, are you sure it is running?`);
94
+ }
95
+ const emulators = {};
96
+ const data = await res.json();
97
+ if (data.database) {
98
+ emulators.database = {
99
+ host: data.database.host,
100
+ port: data.database.port
101
+ };
102
+ }
103
+ if (data.firestore) {
104
+ emulators.firestore = {
105
+ host: data.firestore.host,
106
+ port: data.firestore.port
107
+ };
108
+ }
109
+ if (data.storage) {
110
+ emulators.storage = {
111
+ host: data.storage.host,
112
+ port: data.storage.port
113
+ };
114
+ }
115
+ if (data.hub) {
116
+ emulators.hub = {
117
+ host: data.hub.host,
118
+ port: data.hub.port
119
+ };
120
+ }
121
+ return emulators;
200
122
  }
201
123
  /**
202
124
  * @private
@@ -204,14 +126,14 @@ function discoverEmulators(hub, fetch) {
204
126
  function getEmulatorHostAndPort(emulator, conf, discovered) {
205
127
  var _a, _b;
206
128
  if (conf && ('host' in conf || 'port' in conf)) {
207
- var host = conf.host, port = conf.port;
129
+ const { host, port } = conf;
208
130
  if (host || port) {
209
131
  if (!host || !port) {
210
- throw new Error("Invalid configuration ".concat(emulator, ".host=").concat(host, " and ").concat(emulator, ".port=").concat(port, ". ") +
132
+ throw new Error(`Invalid configuration ${emulator}.host=${host} and ${emulator}.port=${port}. ` +
211
133
  'If either parameter is supplied, both must be defined.');
212
134
  }
213
135
  if (discovered && !discovered[emulator]) {
214
- console.warn("Warning: config for the ".concat(emulator, " emulator is specified, but the Emulator hub ") +
136
+ console.warn(`Warning: config for the ${emulator} emulator is specified, but the Emulator hub ` +
215
137
  'reports it as not running. This may lead to errors such as connection refused.');
216
138
  }
217
139
  return {
@@ -220,12 +142,12 @@ function getEmulatorHostAndPort(emulator, conf, discovered) {
220
142
  };
221
143
  }
222
144
  }
223
- var envVar = EMULATOR_HOST_ENV_VARS[emulator];
224
- var fallback = (discovered === null || discovered === void 0 ? void 0 : discovered[emulator]) || emulatorFromEnvVar(envVar);
145
+ const envVar = EMULATOR_HOST_ENV_VARS[emulator];
146
+ const fallback = (discovered === null || discovered === void 0 ? void 0 : discovered[emulator]) || emulatorFromEnvVar(envVar);
225
147
  if (fallback) {
226
148
  if (discovered && !discovered[emulator]) {
227
- console.warn("Warning: the environment variable ".concat(envVar, " is set, but the Emulator hub reports the ") +
228
- "".concat(emulator, " emulator as not running. This may lead to errors such as connection refused."));
149
+ console.warn(`Warning: the environment variable ${envVar} is set, but the Emulator hub reports the ` +
150
+ `${emulator} emulator as not running. This may lead to errors such as connection refused.`);
229
151
  }
230
152
  return {
231
153
  host: fixHostname(fallback.host, (_b = discovered === null || discovered === void 0 ? void 0 : discovered.hub) === null || _b === void 0 ? void 0 : _b.host),
@@ -234,30 +156,30 @@ function getEmulatorHostAndPort(emulator, conf, discovered) {
234
156
  }
235
157
  }
236
158
  // Visible for testing.
237
- var EMULATOR_HOST_ENV_VARS = {
159
+ const EMULATOR_HOST_ENV_VARS = {
238
160
  'database': 'FIREBASE_DATABASE_EMULATOR_HOST',
239
161
  'firestore': 'FIRESTORE_EMULATOR_HOST',
240
162
  'hub': 'FIREBASE_EMULATOR_HUB',
241
163
  'storage': 'FIREBASE_STORAGE_EMULATOR_HOST'
242
164
  };
243
165
  function emulatorFromEnvVar(envVar) {
244
- var hostAndPort = process.env[envVar];
166
+ const hostAndPort = process.env[envVar];
245
167
  if (!hostAndPort) {
246
168
  return undefined;
247
169
  }
248
- var parsed;
170
+ let parsed;
249
171
  try {
250
- parsed = new URL("http://".concat(hostAndPort));
172
+ parsed = new URL(`http://${hostAndPort}`);
251
173
  }
252
174
  catch (_a) {
253
- throw new Error("Invalid format in environment variable ".concat(envVar, "=").concat(hostAndPort, " (expected host:port)"));
175
+ throw new Error(`Invalid format in environment variable ${envVar}=${hostAndPort} (expected host:port)`);
254
176
  }
255
- var host = parsed.hostname;
256
- var port = Number(parsed.port || '80');
177
+ let host = parsed.hostname;
178
+ const port = Number(parsed.port || '80');
257
179
  if (!Number.isInteger(port)) {
258
- throw new Error("Invalid port in environment variable ".concat(envVar, "=").concat(hostAndPort));
180
+ throw new Error(`Invalid port in environment variable ${envVar}=${hostAndPort}`);
259
181
  }
260
- return { host: host, port: port };
182
+ return { host, port };
261
183
  }
262
184
 
263
185
  /**
@@ -281,172 +203,135 @@ function emulatorFromEnvVar(envVar) {
281
203
  * which should never be directly called by the developer.
282
204
  * @private
283
205
  */
284
- var RulesTestEnvironmentImpl = /** @class */ (function () {
285
- function RulesTestEnvironmentImpl(projectId, emulators) {
206
+ class RulesTestEnvironmentImpl {
207
+ constructor(projectId, emulators) {
286
208
  this.projectId = projectId;
287
209
  this.emulators = emulators;
288
210
  this.contexts = new Set();
289
211
  this.destroyed = false;
290
212
  }
291
- RulesTestEnvironmentImpl.prototype.authenticatedContext = function (user_id, tokenOptions) {
213
+ authenticatedContext(user_id, tokenOptions) {
292
214
  this.checkNotDestroyed();
293
- return this.createContext(__assign(__assign({}, tokenOptions), { sub: user_id, user_id: user_id }));
294
- };
295
- RulesTestEnvironmentImpl.prototype.unauthenticatedContext = function () {
215
+ return this.createContext(Object.assign(Object.assign({}, tokenOptions), { sub: user_id, user_id: user_id }));
216
+ }
217
+ unauthenticatedContext() {
296
218
  this.checkNotDestroyed();
297
219
  return this.createContext(/* authToken = */ undefined);
298
- };
299
- RulesTestEnvironmentImpl.prototype.withSecurityRulesDisabled = function (callback) {
300
- return __awaiter(this, void 0, void 0, function () {
301
- var context;
302
- return __generator(this, function (_a) {
303
- switch (_a.label) {
304
- case 0:
305
- this.checkNotDestroyed();
306
- context = this.createContext('owner');
307
- _a.label = 1;
308
- case 1:
309
- _a.trys.push([1, , 3, 4]);
310
- return [4 /*yield*/, callback(context)];
311
- case 2:
312
- _a.sent();
313
- return [3 /*break*/, 4];
314
- case 3:
315
- // We eagarly clean up this context to actively prevent misuse outside of the callback, e.g.
316
- // storing the context in a variable.
317
- context.cleanup();
318
- this.contexts.delete(context);
319
- return [7 /*endfinally*/];
320
- case 4: return [2 /*return*/];
321
- }
322
- });
323
- });
324
- };
325
- RulesTestEnvironmentImpl.prototype.createContext = function (authToken) {
326
- var context = new RulesTestContextImpl(this.projectId, this.emulators, authToken);
220
+ }
221
+ async withSecurityRulesDisabled(callback) {
222
+ this.checkNotDestroyed();
223
+ // The "owner" token is recognized by the emulators as a special value that bypasses Security
224
+ // Rules. This should only ever be used in withSecurityRulesDisabled.
225
+ // If you're reading this and thinking about doing this in your own app / tests / scripts, think
226
+ // twice. Instead, just use withSecurityRulesDisabled for unit testing OR connect your Firebase
227
+ // Admin SDKs to the emulators for integration testing via environment variables.
228
+ // See: https://firebase.google.com/docs/emulator-suite/connect_firestore#admin_sdks
229
+ const context = this.createContext('owner');
230
+ try {
231
+ await callback(context);
232
+ }
233
+ finally {
234
+ // We eagerly clean up this context to actively prevent misuse outside of the callback, e.g.
235
+ // storing the context in a variable.
236
+ context.cleanup();
237
+ this.contexts.delete(context);
238
+ }
239
+ }
240
+ createContext(authToken) {
241
+ const context = new RulesTestContextImpl(this.projectId, this.emulators, authToken);
327
242
  this.contexts.add(context);
328
243
  return context;
329
- };
330
- RulesTestEnvironmentImpl.prototype.clearDatabase = function () {
244
+ }
245
+ clearDatabase() {
331
246
  this.checkNotDestroyed();
332
- return this.withSecurityRulesDisabled(function (context) {
247
+ return this.withSecurityRulesDisabled(context => {
333
248
  return context.database().ref('/').set(null);
334
249
  });
335
- };
336
- RulesTestEnvironmentImpl.prototype.clearFirestore = function () {
337
- return __awaiter(this, void 0, void 0, function () {
338
- var resp, _a;
339
- return __generator(this, function (_b) {
340
- switch (_b.label) {
341
- case 0:
342
- this.checkNotDestroyed();
343
- assertEmulatorRunning(this.emulators, 'firestore');
344
- return [4 /*yield*/, fetch__default["default"](makeUrl(this.emulators.firestore, "/emulator/v1/projects/".concat(this.projectId, "/databases/(default)/documents")), {
345
- method: 'DELETE'
346
- })];
347
- case 1:
348
- resp = _b.sent();
349
- if (!!resp.ok) return [3 /*break*/, 3];
350
- _a = Error.bind;
351
- return [4 /*yield*/, resp.text()];
352
- case 2: throw new (_a.apply(Error, [void 0, _b.sent()]))();
353
- case 3: return [2 /*return*/];
354
- }
355
- });
250
+ }
251
+ async clearFirestore() {
252
+ this.checkNotDestroyed();
253
+ assertEmulatorRunning(this.emulators, 'firestore');
254
+ const resp = await fetch(makeUrl(this.emulators.firestore, `/emulator/v1/projects/${this.projectId}/databases/(default)/documents`), {
255
+ method: 'DELETE'
356
256
  });
357
- };
358
- RulesTestEnvironmentImpl.prototype.clearStorage = function () {
359
- var _this = this;
257
+ if (!resp.ok) {
258
+ throw new Error(await resp.text());
259
+ }
260
+ }
261
+ clearStorage() {
360
262
  this.checkNotDestroyed();
361
- return this.withSecurityRulesDisabled(function (context) { return __awaiter(_this, void 0, void 0, function () {
362
- var items;
363
- return __generator(this, function (_a) {
364
- switch (_a.label) {
365
- case 0: return [4 /*yield*/, context.storage().ref().listAll()];
366
- case 1:
367
- items = (_a.sent()).items;
368
- return [4 /*yield*/, Promise.all(items.map(function (item) {
369
- return item.delete();
370
- }))];
371
- case 2:
372
- _a.sent();
373
- return [2 /*return*/];
374
- }
375
- });
376
- }); });
377
- };
378
- RulesTestEnvironmentImpl.prototype.cleanup = function () {
379
- return __awaiter(this, void 0, void 0, function () {
380
- return __generator(this, function (_a) {
381
- this.destroyed = true;
382
- this.contexts.forEach(function (context) {
383
- context.envDestroyed = true;
384
- context.cleanup();
385
- });
386
- this.contexts.clear();
387
- return [2 /*return*/];
388
- });
263
+ return this.withSecurityRulesDisabled(async (context) => {
264
+ const { items } = await context.storage().ref().listAll();
265
+ await Promise.all(items.map((item) => {
266
+ return item.delete();
267
+ }));
389
268
  });
390
- };
391
- RulesTestEnvironmentImpl.prototype.checkNotDestroyed = function () {
269
+ }
270
+ async cleanup() {
271
+ this.destroyed = true;
272
+ this.contexts.forEach(context => {
273
+ context.envDestroyed = true;
274
+ context.cleanup();
275
+ });
276
+ this.contexts.clear();
277
+ }
278
+ checkNotDestroyed() {
392
279
  if (this.destroyed) {
393
280
  throw new Error('This RulesTestEnvironment has already been cleaned up. ' +
394
281
  '(This may indicate a leak or missing `await` in your test cases. If you do intend to ' +
395
282
  'perform more tests, please call cleanup() later or create another RulesTestEnvironment.)');
396
283
  }
397
- };
398
- return RulesTestEnvironmentImpl;
399
- }());
284
+ }
285
+ }
400
286
  /**
401
287
  * An implementation of {@code RulesTestContext}. This is private to hide the constructor,
402
288
  * which should never be directly called by the developer.
403
289
  * @private
404
290
  */
405
- var RulesTestContextImpl = /** @class */ (function () {
406
- function RulesTestContextImpl(projectId, emulators, authToken) {
291
+ class RulesTestContextImpl {
292
+ constructor(projectId, emulators, authToken) {
407
293
  this.projectId = projectId;
408
294
  this.emulators = emulators;
409
295
  this.authToken = authToken;
410
296
  this.destroyed = false;
411
297
  this.envDestroyed = false;
412
298
  }
413
- RulesTestContextImpl.prototype.cleanup = function () {
299
+ cleanup() {
414
300
  var _a;
415
301
  this.destroyed = true;
416
302
  (_a = this.app) === null || _a === void 0 ? void 0 : _a.delete();
417
303
  this.app = undefined;
418
- };
419
- RulesTestContextImpl.prototype.firestore = function (settings) {
304
+ }
305
+ firestore(settings) {
420
306
  assertEmulatorRunning(this.emulators, 'firestore');
421
- var firestore = this.getApp().firestore();
307
+ const firestore = this.getApp().firestore();
422
308
  if (settings) {
423
309
  firestore.settings(settings);
424
310
  }
425
311
  firestore.useEmulator(this.emulators.firestore.host, this.emulators.firestore.port, { mockUserToken: this.authToken });
426
312
  return firestore;
427
- };
428
- RulesTestContextImpl.prototype.database = function (databaseURL) {
313
+ }
314
+ database(databaseURL) {
429
315
  assertEmulatorRunning(this.emulators, 'database');
430
316
  if (!databaseURL) {
431
- var url = makeUrl(this.emulators.database, '');
317
+ const url = makeUrl(this.emulators.database, '');
432
318
  // Make sure to set the namespace equal to projectId -- otherwise the RTDB SDK will by default
433
319
  // use `${projectId}-default-rtdb`, which is treated as a different DB by the RTDB emulator
434
320
  // (and thus WON'T apply any rules set for the `projectId` DB during initialization).
435
321
  url.searchParams.append('ns', this.projectId);
436
322
  databaseURL = url.toString();
437
323
  }
438
- var database = this.getApp().database(databaseURL);
324
+ const database = this.getApp().database(databaseURL);
439
325
  database.useEmulator(this.emulators.database.host, this.emulators.database.port, { mockUserToken: this.authToken });
440
326
  return database;
441
- };
442
- RulesTestContextImpl.prototype.storage = function (bucketUrl) {
443
- if (bucketUrl === void 0) { bucketUrl = "gs://".concat(this.projectId); }
327
+ }
328
+ storage(bucketUrl = `gs://${this.projectId}`) {
444
329
  assertEmulatorRunning(this.emulators, 'storage');
445
- var storage = this.getApp().storage(bucketUrl);
330
+ const storage = this.getApp().storage(bucketUrl);
446
331
  storage.useEmulator(this.emulators.storage.host, this.emulators.storage.port, { mockUserToken: this.authToken });
447
332
  return storage;
448
- };
449
- RulesTestContextImpl.prototype.getApp = function () {
333
+ }
334
+ getApp() {
450
335
  if (this.envDestroyed) {
451
336
  throw new Error('This RulesTestContext is no longer valid because its RulesTestEnvironment has been ' +
452
337
  'cleaned up. (This may indicate a leak or missing `await` in your test cases.)');
@@ -457,22 +342,21 @@ var RulesTestContextImpl = /** @class */ (function () {
457
342
  'return a Promise that resolves when the operations are done.');
458
343
  }
459
344
  if (!this.app) {
460
- this.app = firebase__default["default"].initializeApp({ projectId: this.projectId }, "_Firebase_RulesUnitTesting_".concat(Date.now(), "_").concat(Math.random()));
345
+ this.app = firebase__default["default"].initializeApp({ projectId: this.projectId }, `_Firebase_RulesUnitTesting_${Date.now()}_${Math.random()}`);
461
346
  }
462
347
  return this.app;
463
- };
464
- return RulesTestContextImpl;
465
- }());
348
+ }
349
+ }
466
350
  function assertEmulatorRunning(emulators, emulator) {
467
351
  if (!emulators[emulator]) {
468
352
  if (emulators.hub) {
469
- throw new Error("The ".concat(emulator, " emulator is not running (according to Emulator hub). To force ") +
353
+ throw new Error(`The ${emulator} emulator is not running (according to Emulator hub). To force ` +
470
354
  'connecting anyway, please specify its host and port in initializeTestEnvironment({...}).');
471
355
  }
472
356
  else {
473
- throw new Error("The host and port of the ".concat(emulator, " emulator must be specified. (You may wrap the test ") +
357
+ throw new Error(`The host and port of the ${emulator} emulator must be specified. (You may wrap the test ` +
474
358
  "script with `firebase emulators:exec './your-test-script'` to enable automatic " +
475
- "discovery, or specify manually via initializeTestEnvironment({".concat(emulator, ": {host, port}})."));
359
+ `discovery, or specify manually via initializeTestEnvironment({${emulator}: {host, port}}).`);
476
360
  }
477
361
  }
478
362
  }
@@ -496,86 +380,52 @@ function assertEmulatorRunning(emulators, emulator) {
496
380
  /**
497
381
  * @private
498
382
  */
499
- function loadDatabaseRules(hostAndPort, databaseName, rules) {
500
- return __awaiter(this, void 0, void 0, function () {
501
- var url, resp, _a;
502
- return __generator(this, function (_b) {
503
- switch (_b.label) {
504
- case 0:
505
- url = makeUrl(hostAndPort, '/.settings/rules.json');
506
- url.searchParams.append('ns', databaseName);
507
- return [4 /*yield*/, fetch__default["default"](url, {
508
- method: 'PUT',
509
- headers: { Authorization: 'Bearer owner' },
510
- body: rules
511
- })];
512
- case 1:
513
- resp = _b.sent();
514
- if (!!resp.ok) return [3 /*break*/, 3];
515
- _a = Error.bind;
516
- return [4 /*yield*/, resp.text()];
517
- case 2: throw new (_a.apply(Error, [void 0, _b.sent()]))();
518
- case 3: return [2 /*return*/];
519
- }
520
- });
383
+ async function loadDatabaseRules(hostAndPort, databaseName, rules) {
384
+ const url = makeUrl(hostAndPort, '/.settings/rules.json');
385
+ url.searchParams.append('ns', databaseName);
386
+ const resp = await fetch(url, {
387
+ method: 'PUT',
388
+ headers: { Authorization: 'Bearer owner' },
389
+ body: rules
521
390
  });
391
+ if (!resp.ok) {
392
+ throw new Error(await resp.text());
393
+ }
522
394
  }
523
395
  /**
524
396
  * @private
525
397
  */
526
- function loadFirestoreRules(hostAndPort, projectId, rules) {
527
- return __awaiter(this, void 0, void 0, function () {
528
- var resp, _a;
529
- return __generator(this, function (_b) {
530
- switch (_b.label) {
531
- case 0: return [4 /*yield*/, fetch__default["default"](makeUrl(hostAndPort, "/emulator/v1/projects/".concat(projectId, ":securityRules")), {
532
- method: 'PUT',
533
- body: JSON.stringify({
534
- rules: {
535
- files: [{ content: rules }]
536
- }
537
- })
538
- })];
539
- case 1:
540
- resp = _b.sent();
541
- if (!!resp.ok) return [3 /*break*/, 3];
542
- _a = Error.bind;
543
- return [4 /*yield*/, resp.text()];
544
- case 2: throw new (_a.apply(Error, [void 0, _b.sent()]))();
545
- case 3: return [2 /*return*/];
398
+ async function loadFirestoreRules(hostAndPort, projectId, rules) {
399
+ const resp = await fetch(makeUrl(hostAndPort, `/emulator/v1/projects/${projectId}:securityRules`), {
400
+ method: 'PUT',
401
+ body: JSON.stringify({
402
+ rules: {
403
+ files: [{ content: rules }]
546
404
  }
547
- });
405
+ })
548
406
  });
407
+ if (!resp.ok) {
408
+ throw new Error(await resp.text());
409
+ }
549
410
  }
550
411
  /**
551
412
  * @private
552
413
  */
553
- function loadStorageRules(hostAndPort, rules) {
554
- return __awaiter(this, void 0, void 0, function () {
555
- var resp, _a;
556
- return __generator(this, function (_b) {
557
- switch (_b.label) {
558
- case 0: return [4 /*yield*/, fetch__default["default"](makeUrl(hostAndPort, '/internal/setRules'), {
559
- method: 'PUT',
560
- headers: {
561
- 'Content-Type': 'application/json'
562
- },
563
- body: JSON.stringify({
564
- rules: {
565
- files: [{ name: 'storage.rules', content: rules }]
566
- }
567
- })
568
- })];
569
- case 1:
570
- resp = _b.sent();
571
- if (!!resp.ok) return [3 /*break*/, 3];
572
- _a = Error.bind;
573
- return [4 /*yield*/, resp.text()];
574
- case 2: throw new (_a.apply(Error, [void 0, _b.sent()]))();
575
- case 3: return [2 /*return*/];
414
+ async function loadStorageRules(hostAndPort, rules) {
415
+ const resp = await fetch(makeUrl(hostAndPort, '/internal/setRules'), {
416
+ method: 'PUT',
417
+ headers: {
418
+ 'Content-Type': 'application/json'
419
+ },
420
+ body: JSON.stringify({
421
+ rules: {
422
+ files: [{ name: 'storage.rules', content: rules }]
576
423
  }
577
- });
424
+ })
578
425
  });
426
+ if (!resp.ok) {
427
+ throw new Error(await resp.text());
428
+ }
579
429
  }
580
430
 
581
431
  /**
@@ -615,67 +465,40 @@ function loadStorageRules(hostAndPort, rules) {
615
465
  * });
616
466
  * ```
617
467
  */
618
- function initializeTestEnvironment(config) {
468
+ async function initializeTestEnvironment(config) {
619
469
  var _a, _b, _c;
620
- return __awaiter(this, void 0, void 0, function () {
621
- var projectId, hub, discovered, _d, _e, emulators, _i, SUPPORTED_EMULATORS_1, emulator, hostAndPort;
622
- return __generator(this, function (_f) {
623
- switch (_f.label) {
624
- case 0:
625
- projectId = config.projectId || process.env.GCLOUD_PROJECT;
626
- if (!projectId) {
627
- throw new Error('Missing projectId option or env var GCLOUD_PROJECT! Please specify the projectId either ' +
628
- 'way.\n(A demo-* projectId is strongly recommended for unit tests, such as "demo-test".)');
629
- }
630
- hub = getEmulatorHostAndPort('hub', config.hub);
631
- if (!hub) return [3 /*break*/, 2];
632
- _e = [{}];
633
- return [4 /*yield*/, discoverEmulators(hub)];
634
- case 1:
635
- _d = __assign.apply(void 0, [__assign.apply(void 0, _e.concat([(_f.sent())])), { hub: hub }]);
636
- return [3 /*break*/, 3];
637
- case 2:
638
- _d = undefined;
639
- _f.label = 3;
640
- case 3:
641
- discovered = _d;
642
- emulators = {};
643
- if (hub) {
644
- emulators.hub = hub;
645
- }
646
- for (_i = 0, SUPPORTED_EMULATORS_1 = SUPPORTED_EMULATORS; _i < SUPPORTED_EMULATORS_1.length; _i++) {
647
- emulator = SUPPORTED_EMULATORS_1[_i];
648
- hostAndPort = getEmulatorHostAndPort(emulator, config[emulator], discovered);
649
- if (hostAndPort) {
650
- emulators[emulator] = hostAndPort;
651
- }
652
- }
653
- if (!((_a = config.database) === null || _a === void 0 ? void 0 : _a.rules)) return [3 /*break*/, 5];
654
- assertEmulatorRunning(emulators, 'database');
655
- return [4 /*yield*/, loadDatabaseRules(emulators.database, projectId, config.database.rules)];
656
- case 4:
657
- _f.sent();
658
- _f.label = 5;
659
- case 5:
660
- if (!((_b = config.firestore) === null || _b === void 0 ? void 0 : _b.rules)) return [3 /*break*/, 7];
661
- assertEmulatorRunning(emulators, 'firestore');
662
- return [4 /*yield*/, loadFirestoreRules(emulators.firestore, projectId, config.firestore.rules)];
663
- case 6:
664
- _f.sent();
665
- _f.label = 7;
666
- case 7:
667
- if (!((_c = config.storage) === null || _c === void 0 ? void 0 : _c.rules)) return [3 /*break*/, 9];
668
- assertEmulatorRunning(emulators, 'storage');
669
- return [4 /*yield*/, loadStorageRules(emulators.storage, config.storage.rules)];
670
- case 8:
671
- _f.sent();
672
- _f.label = 9;
673
- case 9: return [2 /*return*/, new RulesTestEnvironmentImpl(projectId, emulators)];
674
- }
675
- });
676
- });
470
+ const projectId = config.projectId || process.env.GCLOUD_PROJECT;
471
+ if (!projectId) {
472
+ throw new Error('Missing projectId option or env var GCLOUD_PROJECT! Please specify the projectId either ' +
473
+ 'way.\n(A demo-* projectId is strongly recommended for unit tests, such as "demo-test".)');
474
+ }
475
+ const hub = getEmulatorHostAndPort('hub', config.hub);
476
+ let discovered = hub ? Object.assign(Object.assign({}, (await discoverEmulators(hub))), { hub }) : undefined;
477
+ const emulators = {};
478
+ if (hub) {
479
+ emulators.hub = hub;
480
+ }
481
+ for (const emulator of SUPPORTED_EMULATORS) {
482
+ const hostAndPort = getEmulatorHostAndPort(emulator, config[emulator], discovered);
483
+ if (hostAndPort) {
484
+ emulators[emulator] = hostAndPort;
485
+ }
486
+ }
487
+ if ((_a = config.database) === null || _a === void 0 ? void 0 : _a.rules) {
488
+ assertEmulatorRunning(emulators, 'database');
489
+ await loadDatabaseRules(emulators.database, projectId, config.database.rules);
490
+ }
491
+ if ((_b = config.firestore) === null || _b === void 0 ? void 0 : _b.rules) {
492
+ assertEmulatorRunning(emulators, 'firestore');
493
+ await loadFirestoreRules(emulators.firestore, projectId, config.firestore.rules);
494
+ }
495
+ if ((_c = config.storage) === null || _c === void 0 ? void 0 : _c.rules) {
496
+ assertEmulatorRunning(emulators, 'storage');
497
+ await loadStorageRules(emulators.storage, config.storage.rules);
498
+ }
499
+ return new RulesTestEnvironmentImpl(projectId, emulators);
677
500
  }
678
- var SUPPORTED_EMULATORS = ['database', 'firestore', 'storage'];
501
+ const SUPPORTED_EMULATORS = ['database', 'firestore', 'storage'];
679
502
 
680
503
  /**
681
504
  * @license
@@ -693,59 +516,47 @@ var SUPPORTED_EMULATORS = ['database', 'firestore', 'storage'];
693
516
  * See the License for the specific language governing permissions and
694
517
  * limitations under the License.
695
518
  */
696
- function withFunctionTriggersDisabled(fnOrHub, maybeFn) {
697
- return __awaiter(this, void 0, void 0, function () {
698
- var hub, disableRes, result, enableRes;
699
- return __generator(this, function (_a) {
700
- switch (_a.label) {
701
- case 0:
702
- if (typeof fnOrHub === 'function') {
703
- maybeFn = fnOrHub;
704
- hub = getEmulatorHostAndPort('hub');
705
- }
706
- else {
707
- hub = getEmulatorHostAndPort('hub', fnOrHub);
708
- if (!maybeFn) {
709
- throw new Error('The callback function must be specified!');
710
- }
711
- }
712
- if (!hub) {
713
- throw new Error('Please specify the Emulator Hub host and port via arguments or set the environment ' +
714
- "varible ".concat(EMULATOR_HOST_ENV_VARS.hub, "!"));
715
- }
716
- hub.host = fixHostname(hub.host);
717
- makeUrl(hub, '/functions/disableBackgroundTriggers');
718
- return [4 /*yield*/, fetch__default["default"](makeUrl(hub, '/functions/disableBackgroundTriggers'), {
719
- method: 'PUT'
720
- })];
721
- case 1:
722
- disableRes = _a.sent();
723
- if (!disableRes.ok) {
724
- throw new Error("HTTP Error ".concat(disableRes.status, " when disabling functions triggers, are you using firebase-tools 8.13.0 or higher?"));
725
- }
726
- result = undefined;
727
- _a.label = 2;
728
- case 2:
729
- _a.trys.push([2, , 4, 6]);
730
- return [4 /*yield*/, maybeFn()];
731
- case 3:
732
- result = _a.sent();
733
- return [3 /*break*/, 6];
734
- case 4: return [4 /*yield*/, fetch__default["default"](makeUrl(hub, '/functions/enableBackgroundTriggers'), {
735
- method: 'PUT'
736
- })];
737
- case 5:
738
- enableRes = _a.sent();
739
- if (!enableRes.ok) {
740
- throw new Error("HTTP Error ".concat(enableRes.status, " when enabling functions triggers, are you using firebase-tools 8.13.0 or higher?"));
741
- }
742
- return [7 /*endfinally*/];
743
- case 6:
744
- // Return the user's function result
745
- return [2 /*return*/, result];
746
- }
747
- });
519
+ async function withFunctionTriggersDisabled(fnOrHub, maybeFn) {
520
+ let hub;
521
+ if (typeof fnOrHub === 'function') {
522
+ maybeFn = fnOrHub;
523
+ hub = getEmulatorHostAndPort('hub');
524
+ }
525
+ else {
526
+ hub = getEmulatorHostAndPort('hub', fnOrHub);
527
+ if (!maybeFn) {
528
+ throw new Error('The callback function must be specified!');
529
+ }
530
+ }
531
+ if (!hub) {
532
+ throw new Error('Please specify the Emulator Hub host and port via arguments or set the environment ' +
533
+ `variable ${EMULATOR_HOST_ENV_VARS.hub}!`);
534
+ }
535
+ hub.host = fixHostname(hub.host);
536
+ makeUrl(hub, '/functions/disableBackgroundTriggers');
537
+ // Disable background triggers
538
+ const disableRes = await fetch(makeUrl(hub, '/functions/disableBackgroundTriggers'), {
539
+ method: 'PUT'
748
540
  });
541
+ if (!disableRes.ok) {
542
+ throw new Error(`HTTP Error ${disableRes.status} when disabling functions triggers, are you using firebase-tools 8.13.0 or higher?`);
543
+ }
544
+ // Run the user's function
545
+ let result = undefined;
546
+ try {
547
+ result = await maybeFn();
548
+ }
549
+ finally {
550
+ // Re-enable background triggers
551
+ const enableRes = await fetch(makeUrl(hub, '/functions/enableBackgroundTriggers'), {
552
+ method: 'PUT'
553
+ });
554
+ if (!enableRes.ok) {
555
+ throw new Error(`HTTP Error ${enableRes.status} when enabling functions triggers, are you using firebase-tools 8.13.0 or higher?`);
556
+ }
557
+ }
558
+ // Return the user's function result
559
+ return result;
749
560
  }
750
561
  /**
751
562
  * Assert the promise to be rejected with a "permission denied" error.
@@ -764,20 +575,20 @@ function withFunctionTriggersDisabled(fnOrHub, maybeFn) {
764
575
  * ```
765
576
  */
766
577
  function assertFails(pr) {
767
- return pr.then(function () {
578
+ return pr.then(() => {
768
579
  return Promise.reject(new Error('Expected request to fail, but it succeeded.'));
769
- }, function (err) {
580
+ }, (err) => {
770
581
  var _a, _b;
771
- var errCode = ((_a = err === null || err === void 0 ? void 0 : err.code) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '';
772
- var errMessage = ((_b = err === null || err === void 0 ? void 0 : err.message) === null || _b === void 0 ? void 0 : _b.toLowerCase()) || '';
773
- var isPermissionDenied = errCode === 'permission-denied' ||
582
+ const errCode = ((_a = err === null || err === void 0 ? void 0 : err.code) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '';
583
+ const errMessage = ((_b = err === null || err === void 0 ? void 0 : err.message) === null || _b === void 0 ? void 0 : _b.toLowerCase()) || '';
584
+ const isPermissionDenied = errCode === 'permission-denied' ||
774
585
  errCode === 'permission_denied' ||
775
586
  errMessage.indexOf('permission_denied') >= 0 ||
776
587
  errMessage.indexOf('permission denied') >= 0 ||
777
588
  // Storage permission errors contain message: (storage/unauthorized)
778
589
  errMessage.indexOf('unauthorized') >= 0;
779
590
  if (!isPermissionDenied) {
780
- return Promise.reject(new Error("Expected PERMISSION_DENIED but got unexpected error: ".concat(err)));
591
+ return Promise.reject(new Error(`Expected PERMISSION_DENIED but got unexpected error: ${err}`));
781
592
  }
782
593
  return err;
783
594
  });