@firebase/rules-unit-testing 4.0.0 → 4.0.1

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
@@ -11,603 +11,603 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
11
11
 
12
12
  var firebase__default = /*#__PURE__*/_interopDefaultLegacy(firebase);
13
13
 
14
- /**
15
- * @license
16
- * Copyright 2021 Google LLC
17
- *
18
- * Licensed under the Apache License, Version 2.0 (the "License");
19
- * you may not use this file except in compliance with the License.
20
- * You may obtain a copy of the License at
21
- *
22
- * http://www.apache.org/licenses/LICENSE-2.0
23
- *
24
- * Unless required by applicable law or agreed to in writing, software
25
- * distributed under the License is distributed on an "AS IS" BASIS,
26
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27
- * See the License for the specific language governing permissions and
28
- * limitations under the License.
29
- */
30
- /**
31
- * Return a connectable hostname, replacing wildcard 0.0.0.0 or :: with loopback
32
- * addresses 127.0.0.1 / ::1 correspondingly. See below for why this is needed:
33
- * https://github.com/firebase/firebase-tools-ui/issues/286
34
- *
35
- * This assumes emulators are running on the same device as fallbackHost (e.g.
36
- * hub), which should hold if both are started from the same CLI command.
37
- * @private
38
- */
39
- function fixHostname(host, fallbackHost) {
40
- host = host.replace('[', '').replace(']', ''); // Remove IPv6 brackets
41
- if (host === '0.0.0.0') {
42
- host = fallbackHost || '127.0.0.1';
43
- }
44
- else if (host === '::') {
45
- host = fallbackHost || '::1';
46
- }
47
- return host;
48
- }
49
- /**
50
- * Create a URL with host, port, and path. Handles IPv6 bracketing correctly.
51
- * @private
52
- */
53
- function makeUrl(hostAndPort, path) {
54
- if (typeof hostAndPort === 'object') {
55
- const { host, port } = hostAndPort;
56
- if (host.includes(':')) {
57
- hostAndPort = `[${host}]:${port}`;
58
- }
59
- else {
60
- hostAndPort = `${host}:${port}`;
61
- }
62
- }
63
- const url = new URL(`http://${hostAndPort}/`);
64
- url.pathname = path;
65
- return url;
14
+ /**
15
+ * @license
16
+ * Copyright 2021 Google LLC
17
+ *
18
+ * Licensed under the Apache License, Version 2.0 (the "License");
19
+ * you may not use this file except in compliance with the License.
20
+ * You may obtain a copy of the License at
21
+ *
22
+ * http://www.apache.org/licenses/LICENSE-2.0
23
+ *
24
+ * Unless required by applicable law or agreed to in writing, software
25
+ * distributed under the License is distributed on an "AS IS" BASIS,
26
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27
+ * See the License for the specific language governing permissions and
28
+ * limitations under the License.
29
+ */
30
+ /**
31
+ * Return a connectable hostname, replacing wildcard 0.0.0.0 or :: with loopback
32
+ * addresses 127.0.0.1 / ::1 correspondingly. See below for why this is needed:
33
+ * https://github.com/firebase/firebase-tools-ui/issues/286
34
+ *
35
+ * This assumes emulators are running on the same device as fallbackHost (e.g.
36
+ * hub), which should hold if both are started from the same CLI command.
37
+ * @private
38
+ */
39
+ function fixHostname(host, fallbackHost) {
40
+ host = host.replace('[', '').replace(']', ''); // Remove IPv6 brackets
41
+ if (host === '0.0.0.0') {
42
+ host = fallbackHost || '127.0.0.1';
43
+ }
44
+ else if (host === '::') {
45
+ host = fallbackHost || '::1';
46
+ }
47
+ return host;
48
+ }
49
+ /**
50
+ * Create a URL with host, port, and path. Handles IPv6 bracketing correctly.
51
+ * @private
52
+ */
53
+ function makeUrl(hostAndPort, path) {
54
+ if (typeof hostAndPort === 'object') {
55
+ const { host, port } = hostAndPort;
56
+ if (host.includes(':')) {
57
+ hostAndPort = `[${host}]:${port}`;
58
+ }
59
+ else {
60
+ hostAndPort = `${host}:${port}`;
61
+ }
62
+ }
63
+ const url = new URL(`http://${hostAndPort}/`);
64
+ url.pathname = path;
65
+ return url;
66
66
  }
67
67
 
68
- /**
69
- * @license
70
- * Copyright 2021 Google LLC
71
- *
72
- * Licensed under the Apache License, Version 2.0 (the "License");
73
- * you may not use this file except in compliance with the License.
74
- * You may obtain a copy of the License at
75
- *
76
- * http://www.apache.org/licenses/LICENSE-2.0
77
- *
78
- * Unless required by applicable law or agreed to in writing, software
79
- * distributed under the License is distributed on an "AS IS" BASIS,
80
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
81
- * See the License for the specific language governing permissions and
82
- * limitations under the License.
83
- */
84
- /**
85
- * Use the Firebase Emulator hub to discover other running emulators.
86
- *
87
- * @param hub the host and port where the Emulator Hub is running
88
- * @private
89
- */
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;
122
- }
123
- /**
124
- * @private
125
- */
126
- function getEmulatorHostAndPort(emulator, conf, discovered) {
127
- var _a, _b;
128
- if (conf && ('host' in conf || 'port' in conf)) {
129
- const { host, port } = conf;
130
- if (host || port) {
131
- if (!host || !port) {
132
- throw new Error(`Invalid configuration ${emulator}.host=${host} and ${emulator}.port=${port}. ` +
133
- 'If either parameter is supplied, both must be defined.');
134
- }
135
- if (discovered && !discovered[emulator]) {
136
- console.warn(`Warning: config for the ${emulator} emulator is specified, but the Emulator hub ` +
137
- 'reports it as not running. This may lead to errors such as connection refused.');
138
- }
139
- return {
140
- host: fixHostname(conf.host, (_a = discovered === null || discovered === void 0 ? void 0 : discovered.hub) === null || _a === void 0 ? void 0 : _a.host),
141
- port: conf.port
142
- };
143
- }
144
- }
145
- const envVar = EMULATOR_HOST_ENV_VARS[emulator];
146
- const fallback = (discovered === null || discovered === void 0 ? void 0 : discovered[emulator]) || emulatorFromEnvVar(envVar);
147
- if (fallback) {
148
- if (discovered && !discovered[emulator]) {
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.`);
151
- }
152
- return {
153
- host: fixHostname(fallback.host, (_b = discovered === null || discovered === void 0 ? void 0 : discovered.hub) === null || _b === void 0 ? void 0 : _b.host),
154
- port: fallback.port
155
- };
156
- }
157
- }
158
- // Visible for testing.
159
- const EMULATOR_HOST_ENV_VARS = {
160
- 'database': 'FIREBASE_DATABASE_EMULATOR_HOST',
161
- 'firestore': 'FIRESTORE_EMULATOR_HOST',
162
- 'hub': 'FIREBASE_EMULATOR_HUB',
163
- 'storage': 'FIREBASE_STORAGE_EMULATOR_HOST'
164
- };
165
- function emulatorFromEnvVar(envVar) {
166
- const hostAndPort = process.env[envVar];
167
- if (!hostAndPort) {
168
- return undefined;
169
- }
170
- let parsed;
171
- try {
172
- parsed = new URL(`http://${hostAndPort}`);
173
- }
174
- catch (_a) {
175
- throw new Error(`Invalid format in environment variable ${envVar}=${hostAndPort} (expected host:port)`);
176
- }
177
- let host = parsed.hostname;
178
- const port = Number(parsed.port || '80');
179
- if (!Number.isInteger(port)) {
180
- throw new Error(`Invalid port in environment variable ${envVar}=${hostAndPort}`);
181
- }
182
- return { host, port };
68
+ /**
69
+ * @license
70
+ * Copyright 2021 Google LLC
71
+ *
72
+ * Licensed under the Apache License, Version 2.0 (the "License");
73
+ * you may not use this file except in compliance with the License.
74
+ * You may obtain a copy of the License at
75
+ *
76
+ * http://www.apache.org/licenses/LICENSE-2.0
77
+ *
78
+ * Unless required by applicable law or agreed to in writing, software
79
+ * distributed under the License is distributed on an "AS IS" BASIS,
80
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
81
+ * See the License for the specific language governing permissions and
82
+ * limitations under the License.
83
+ */
84
+ /**
85
+ * Use the Firebase Emulator hub to discover other running emulators.
86
+ *
87
+ * @param hub the host and port where the Emulator Hub is running
88
+ * @private
89
+ */
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;
122
+ }
123
+ /**
124
+ * @private
125
+ */
126
+ function getEmulatorHostAndPort(emulator, conf, discovered) {
127
+ var _a, _b;
128
+ if (conf && ('host' in conf || 'port' in conf)) {
129
+ const { host, port } = conf;
130
+ if (host || port) {
131
+ if (!host || !port) {
132
+ throw new Error(`Invalid configuration ${emulator}.host=${host} and ${emulator}.port=${port}. ` +
133
+ 'If either parameter is supplied, both must be defined.');
134
+ }
135
+ if (discovered && !discovered[emulator]) {
136
+ console.warn(`Warning: config for the ${emulator} emulator is specified, but the Emulator hub ` +
137
+ 'reports it as not running. This may lead to errors such as connection refused.');
138
+ }
139
+ return {
140
+ host: fixHostname(host, (_a = discovered === null || discovered === void 0 ? void 0 : discovered.hub) === null || _a === void 0 ? void 0 : _a.host),
141
+ port: port
142
+ };
143
+ }
144
+ }
145
+ const envVar = EMULATOR_HOST_ENV_VARS[emulator];
146
+ const fallback = (discovered === null || discovered === void 0 ? void 0 : discovered[emulator]) || emulatorFromEnvVar(envVar);
147
+ if (fallback) {
148
+ if (discovered && !discovered[emulator]) {
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.`);
151
+ }
152
+ return {
153
+ host: fixHostname(fallback.host, (_b = discovered === null || discovered === void 0 ? void 0 : discovered.hub) === null || _b === void 0 ? void 0 : _b.host),
154
+ port: fallback.port
155
+ };
156
+ }
157
+ }
158
+ // Visible for testing.
159
+ const EMULATOR_HOST_ENV_VARS = {
160
+ 'database': 'FIREBASE_DATABASE_EMULATOR_HOST',
161
+ 'firestore': 'FIRESTORE_EMULATOR_HOST',
162
+ 'hub': 'FIREBASE_EMULATOR_HUB',
163
+ 'storage': 'FIREBASE_STORAGE_EMULATOR_HOST'
164
+ };
165
+ function emulatorFromEnvVar(envVar) {
166
+ const hostAndPort = process.env[envVar];
167
+ if (!hostAndPort) {
168
+ return undefined;
169
+ }
170
+ let parsed;
171
+ try {
172
+ parsed = new URL(`http://${hostAndPort}`);
173
+ }
174
+ catch (_a) {
175
+ throw new Error(`Invalid format in environment variable ${envVar}=${hostAndPort} (expected host:port)`);
176
+ }
177
+ let host = parsed.hostname;
178
+ const port = Number(parsed.port || '80');
179
+ if (!Number.isInteger(port)) {
180
+ throw new Error(`Invalid port in environment variable ${envVar}=${hostAndPort}`);
181
+ }
182
+ return { host, port };
183
183
  }
184
184
 
185
- /**
186
- * @license
187
- * Copyright 2021 Google LLC
188
- *
189
- * Licensed under the Apache License, Version 2.0 (the "License");
190
- * you may not use this file except in compliance with the License.
191
- * You may obtain a copy of the License at
192
- *
193
- * http://www.apache.org/licenses/LICENSE-2.0
194
- *
195
- * Unless required by applicable law or agreed to in writing, software
196
- * distributed under the License is distributed on an "AS IS" BASIS,
197
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
198
- * See the License for the specific language governing permissions and
199
- * limitations under the License.
200
- */
201
- /**
202
- * An implementation of {@code RulesTestEnvironment}. This is private to hide the constructor,
203
- * which should never be directly called by the developer.
204
- * @private
205
- */
206
- class RulesTestEnvironmentImpl {
207
- constructor(projectId, emulators) {
208
- this.projectId = projectId;
209
- this.emulators = emulators;
210
- this.contexts = new Set();
211
- this.destroyed = false;
212
- }
213
- authenticatedContext(user_id, tokenOptions) {
214
- this.checkNotDestroyed();
215
- return this.createContext(Object.assign(Object.assign({}, tokenOptions), { sub: user_id, user_id: user_id }));
216
- }
217
- unauthenticatedContext() {
218
- this.checkNotDestroyed();
219
- return this.createContext(/* authToken = */ undefined);
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);
242
- this.contexts.add(context);
243
- return context;
244
- }
245
- clearDatabase() {
246
- this.checkNotDestroyed();
247
- return this.withSecurityRulesDisabled(context => {
248
- return context.database().ref('/').set(null);
249
- });
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'
256
- });
257
- if (!resp.ok) {
258
- throw new Error(await resp.text());
259
- }
260
- }
261
- clearStorage() {
262
- this.checkNotDestroyed();
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
- }));
268
- });
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() {
279
- if (this.destroyed) {
280
- throw new Error('This RulesTestEnvironment has already been cleaned up. ' +
281
- '(This may indicate a leak or missing `await` in your test cases. If you do intend to ' +
282
- 'perform more tests, please call cleanup() later or create another RulesTestEnvironment.)');
283
- }
284
- }
285
- }
286
- /**
287
- * An implementation of {@code RulesTestContext}. This is private to hide the constructor,
288
- * which should never be directly called by the developer.
289
- * @private
290
- */
291
- class RulesTestContextImpl {
292
- constructor(projectId, emulators, authToken) {
293
- this.projectId = projectId;
294
- this.emulators = emulators;
295
- this.authToken = authToken;
296
- this.destroyed = false;
297
- this.envDestroyed = false;
298
- }
299
- cleanup() {
300
- var _a;
301
- this.destroyed = true;
302
- (_a = this.app) === null || _a === void 0 ? void 0 : _a.delete();
303
- this.app = undefined;
304
- }
305
- firestore(settings) {
306
- assertEmulatorRunning(this.emulators, 'firestore');
307
- const firestore = this.getApp().firestore();
308
- if (settings) {
309
- firestore.settings(settings);
310
- }
311
- firestore.useEmulator(this.emulators.firestore.host, this.emulators.firestore.port, { mockUserToken: this.authToken });
312
- return firestore;
313
- }
314
- database(databaseURL) {
315
- assertEmulatorRunning(this.emulators, 'database');
316
- if (!databaseURL) {
317
- const url = makeUrl(this.emulators.database, '');
318
- // Make sure to set the namespace equal to projectId -- otherwise the RTDB SDK will by default
319
- // use `${projectId}-default-rtdb`, which is treated as a different DB by the RTDB emulator
320
- // (and thus WON'T apply any rules set for the `projectId` DB during initialization).
321
- url.searchParams.append('ns', this.projectId);
322
- databaseURL = url.toString();
323
- }
324
- const database = this.getApp().database(databaseURL);
325
- database.useEmulator(this.emulators.database.host, this.emulators.database.port, { mockUserToken: this.authToken });
326
- return database;
327
- }
328
- storage(bucketUrl = `gs://${this.projectId}`) {
329
- assertEmulatorRunning(this.emulators, 'storage');
330
- const storage = this.getApp().storage(bucketUrl);
331
- storage.useEmulator(this.emulators.storage.host, this.emulators.storage.port, { mockUserToken: this.authToken });
332
- return storage;
333
- }
334
- getApp() {
335
- if (this.envDestroyed) {
336
- throw new Error('This RulesTestContext is no longer valid because its RulesTestEnvironment has been ' +
337
- 'cleaned up. (This may indicate a leak or missing `await` in your test cases.)');
338
- }
339
- if (this.destroyed) {
340
- throw new Error('This RulesTestContext is no longer valid. When using withSecurityRulesDisabled, ' +
341
- 'make sure to perform all operations on the context within the callback function and ' +
342
- 'return a Promise that resolves when the operations are done.');
343
- }
344
- if (!this.app) {
345
- this.app = firebase__default["default"].initializeApp({ projectId: this.projectId }, `_Firebase_RulesUnitTesting_${Date.now()}_${Math.random()}`);
346
- }
347
- return this.app;
348
- }
349
- }
350
- function assertEmulatorRunning(emulators, emulator) {
351
- if (!emulators[emulator]) {
352
- if (emulators.hub) {
353
- throw new Error(`The ${emulator} emulator is not running (according to Emulator hub). To force ` +
354
- 'connecting anyway, please specify its host and port in initializeTestEnvironment({...}).');
355
- }
356
- else {
357
- throw new Error(`The host and port of the ${emulator} emulator must be specified. (You may wrap the test ` +
358
- "script with `firebase emulators:exec './your-test-script'` to enable automatic " +
359
- `discovery, or specify manually via initializeTestEnvironment({${emulator}: {host, port}}).`);
360
- }
361
- }
185
+ /**
186
+ * @license
187
+ * Copyright 2021 Google LLC
188
+ *
189
+ * Licensed under the Apache License, Version 2.0 (the "License");
190
+ * you may not use this file except in compliance with the License.
191
+ * You may obtain a copy of the License at
192
+ *
193
+ * http://www.apache.org/licenses/LICENSE-2.0
194
+ *
195
+ * Unless required by applicable law or agreed to in writing, software
196
+ * distributed under the License is distributed on an "AS IS" BASIS,
197
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
198
+ * See the License for the specific language governing permissions and
199
+ * limitations under the License.
200
+ */
201
+ /**
202
+ * An implementation of {@code RulesTestEnvironment}. This is private to hide the constructor,
203
+ * which should never be directly called by the developer.
204
+ * @private
205
+ */
206
+ class RulesTestEnvironmentImpl {
207
+ constructor(projectId, emulators) {
208
+ this.projectId = projectId;
209
+ this.emulators = emulators;
210
+ this.contexts = new Set();
211
+ this.destroyed = false;
212
+ }
213
+ authenticatedContext(user_id, tokenOptions) {
214
+ this.checkNotDestroyed();
215
+ return this.createContext(Object.assign(Object.assign({}, tokenOptions), { sub: user_id, user_id: user_id }));
216
+ }
217
+ unauthenticatedContext() {
218
+ this.checkNotDestroyed();
219
+ return this.createContext(/* authToken = */ undefined);
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);
242
+ this.contexts.add(context);
243
+ return context;
244
+ }
245
+ clearDatabase() {
246
+ this.checkNotDestroyed();
247
+ return this.withSecurityRulesDisabled(context => {
248
+ return context.database().ref('/').set(null);
249
+ });
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'
256
+ });
257
+ if (!resp.ok) {
258
+ throw new Error(await resp.text());
259
+ }
260
+ }
261
+ clearStorage() {
262
+ this.checkNotDestroyed();
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
+ }));
268
+ });
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() {
279
+ if (this.destroyed) {
280
+ throw new Error('This RulesTestEnvironment has already been cleaned up. ' +
281
+ '(This may indicate a leak or missing `await` in your test cases. If you do intend to ' +
282
+ 'perform more tests, please call cleanup() later or create another RulesTestEnvironment.)');
283
+ }
284
+ }
285
+ }
286
+ /**
287
+ * An implementation of {@code RulesTestContext}. This is private to hide the constructor,
288
+ * which should never be directly called by the developer.
289
+ * @private
290
+ */
291
+ class RulesTestContextImpl {
292
+ constructor(projectId, emulators, authToken) {
293
+ this.projectId = projectId;
294
+ this.emulators = emulators;
295
+ this.authToken = authToken;
296
+ this.destroyed = false;
297
+ this.envDestroyed = false;
298
+ }
299
+ cleanup() {
300
+ var _a;
301
+ this.destroyed = true;
302
+ (_a = this.app) === null || _a === void 0 ? void 0 : _a.delete();
303
+ this.app = undefined;
304
+ }
305
+ firestore(settings) {
306
+ assertEmulatorRunning(this.emulators, 'firestore');
307
+ const firestore = this.getApp().firestore();
308
+ if (settings) {
309
+ firestore.settings(settings);
310
+ }
311
+ firestore.useEmulator(this.emulators.firestore.host, this.emulators.firestore.port, { mockUserToken: this.authToken });
312
+ return firestore;
313
+ }
314
+ database(databaseURL) {
315
+ assertEmulatorRunning(this.emulators, 'database');
316
+ if (!databaseURL) {
317
+ const url = makeUrl(this.emulators.database, '');
318
+ // Make sure to set the namespace equal to projectId -- otherwise the RTDB SDK will by default
319
+ // use `${projectId}-default-rtdb`, which is treated as a different DB by the RTDB emulator
320
+ // (and thus WON'T apply any rules set for the `projectId` DB during initialization).
321
+ url.searchParams.append('ns', this.projectId);
322
+ databaseURL = url.toString();
323
+ }
324
+ const database = this.getApp().database(databaseURL);
325
+ database.useEmulator(this.emulators.database.host, this.emulators.database.port, { mockUserToken: this.authToken });
326
+ return database;
327
+ }
328
+ storage(bucketUrl = `gs://${this.projectId}`) {
329
+ assertEmulatorRunning(this.emulators, 'storage');
330
+ const storage = this.getApp().storage(bucketUrl);
331
+ storage.useEmulator(this.emulators.storage.host, this.emulators.storage.port, { mockUserToken: this.authToken });
332
+ return storage;
333
+ }
334
+ getApp() {
335
+ if (this.envDestroyed) {
336
+ throw new Error('This RulesTestContext is no longer valid because its RulesTestEnvironment has been ' +
337
+ 'cleaned up. (This may indicate a leak or missing `await` in your test cases.)');
338
+ }
339
+ if (this.destroyed) {
340
+ throw new Error('This RulesTestContext is no longer valid. When using withSecurityRulesDisabled, ' +
341
+ 'make sure to perform all operations on the context within the callback function and ' +
342
+ 'return a Promise that resolves when the operations are done.');
343
+ }
344
+ if (!this.app) {
345
+ this.app = firebase__default["default"].initializeApp({ projectId: this.projectId }, `_Firebase_RulesUnitTesting_${Date.now()}_${Math.random()}`);
346
+ }
347
+ return this.app;
348
+ }
349
+ }
350
+ function assertEmulatorRunning(emulators, emulator) {
351
+ if (!emulators[emulator]) {
352
+ if (emulators.hub) {
353
+ throw new Error(`The ${emulator} emulator is not running (according to Emulator hub). To force ` +
354
+ 'connecting anyway, please specify its host and port in initializeTestEnvironment({...}).');
355
+ }
356
+ else {
357
+ throw new Error(`The host and port of the ${emulator} emulator must be specified. (You may wrap the test ` +
358
+ "script with `firebase emulators:exec './your-test-script'` to enable automatic " +
359
+ `discovery, or specify manually via initializeTestEnvironment({${emulator}: {host, port}}).`);
360
+ }
361
+ }
362
362
  }
363
363
 
364
- /**
365
- * @license
366
- * Copyright 2021 Google LLC
367
- *
368
- * Licensed under the Apache License, Version 2.0 (the "License");
369
- * you may not use this file except in compliance with the License.
370
- * You may obtain a copy of the License at
371
- *
372
- * http://www.apache.org/licenses/LICENSE-2.0
373
- *
374
- * Unless required by applicable law or agreed to in writing, software
375
- * distributed under the License is distributed on an "AS IS" BASIS,
376
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
377
- * See the License for the specific language governing permissions and
378
- * limitations under the License.
379
- */
380
- /**
381
- * @private
382
- */
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
390
- });
391
- if (!resp.ok) {
392
- throw new Error(await resp.text());
393
- }
394
- }
395
- /**
396
- * @private
397
- */
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 }]
404
- }
405
- })
406
- });
407
- if (!resp.ok) {
408
- throw new Error(await resp.text());
409
- }
410
- }
411
- /**
412
- * @private
413
- */
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 }]
423
- }
424
- })
425
- });
426
- if (!resp.ok) {
427
- throw new Error(await resp.text());
428
- }
364
+ /**
365
+ * @license
366
+ * Copyright 2021 Google LLC
367
+ *
368
+ * Licensed under the Apache License, Version 2.0 (the "License");
369
+ * you may not use this file except in compliance with the License.
370
+ * You may obtain a copy of the License at
371
+ *
372
+ * http://www.apache.org/licenses/LICENSE-2.0
373
+ *
374
+ * Unless required by applicable law or agreed to in writing, software
375
+ * distributed under the License is distributed on an "AS IS" BASIS,
376
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
377
+ * See the License for the specific language governing permissions and
378
+ * limitations under the License.
379
+ */
380
+ /**
381
+ * @private
382
+ */
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
390
+ });
391
+ if (!resp.ok) {
392
+ throw new Error(await resp.text());
393
+ }
394
+ }
395
+ /**
396
+ * @private
397
+ */
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 }]
404
+ }
405
+ })
406
+ });
407
+ if (!resp.ok) {
408
+ throw new Error(await resp.text());
409
+ }
410
+ }
411
+ /**
412
+ * @private
413
+ */
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 }]
423
+ }
424
+ })
425
+ });
426
+ if (!resp.ok) {
427
+ throw new Error(await resp.text());
428
+ }
429
429
  }
430
430
 
431
- /**
432
- * @license
433
- * Copyright 2021 Google LLC
434
- *
435
- * Licensed under the Apache License, Version 2.0 (the "License");
436
- * you may not use this file except in compliance with the License.
437
- * You may obtain a copy of the License at
438
- *
439
- * http://www.apache.org/licenses/LICENSE-2.0
440
- *
441
- * Unless required by applicable law or agreed to in writing, software
442
- * distributed under the License is distributed on an "AS IS" BASIS,
443
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
444
- * See the License for the specific language governing permissions and
445
- * limitations under the License.
446
- */
447
- /**
448
- * Initializes a test environment for rules unit testing. Call this function first for test setup.
449
- *
450
- * Requires emulators to be running. This function tries to discover those emulators via environment
451
- * variables or through the Firebase Emulator hub if hosts and ports are unspecified. It is strongly
452
- * recommended to specify security rules for emulators used for testing. See minimal example below.
453
- *
454
- * @param config - the configuration for emulators. Most fields are optional if they can be discovered
455
- * @returns a promise that resolves with an environment ready for testing, or rejects on error.
456
- * @public
457
- * @example
458
- * ```javascript
459
- * const testEnv = await initializeTestEnvironment({
460
- * firestore: {
461
- * rules: fs.readFileSync("/path/to/firestore.rules", "utf8"), // Load rules from file
462
- * // host and port can be omitted if they can be discovered from the hub.
463
- * },
464
- * // ...
465
- * });
466
- * ```
467
- */
468
- async function initializeTestEnvironment(config) {
469
- var _a, _b, _c;
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);
500
- }
431
+ /**
432
+ * @license
433
+ * Copyright 2021 Google LLC
434
+ *
435
+ * Licensed under the Apache License, Version 2.0 (the "License");
436
+ * you may not use this file except in compliance with the License.
437
+ * You may obtain a copy of the License at
438
+ *
439
+ * http://www.apache.org/licenses/LICENSE-2.0
440
+ *
441
+ * Unless required by applicable law or agreed to in writing, software
442
+ * distributed under the License is distributed on an "AS IS" BASIS,
443
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
444
+ * See the License for the specific language governing permissions and
445
+ * limitations under the License.
446
+ */
447
+ /**
448
+ * Initializes a test environment for rules unit testing. Call this function first for test setup.
449
+ *
450
+ * Requires emulators to be running. This function tries to discover those emulators via environment
451
+ * variables or through the Firebase Emulator hub if hosts and ports are unspecified. It is strongly
452
+ * recommended to specify security rules for emulators used for testing. See minimal example below.
453
+ *
454
+ * @param config - the configuration for emulators. Most fields are optional if they can be discovered
455
+ * @returns a promise that resolves with an environment ready for testing, or rejects on error.
456
+ * @public
457
+ * @example
458
+ * ```javascript
459
+ * const testEnv = await initializeTestEnvironment({
460
+ * firestore: {
461
+ * rules: fs.readFileSync("/path/to/firestore.rules", "utf8"), // Load rules from file
462
+ * // host and port can be omitted if they can be discovered from the hub.
463
+ * },
464
+ * // ...
465
+ * });
466
+ * ```
467
+ */
468
+ async function initializeTestEnvironment(config) {
469
+ var _a, _b, _c;
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);
500
+ }
501
501
  const SUPPORTED_EMULATORS = ['database', 'firestore', 'storage'];
502
502
 
503
- /**
504
- * @license
505
- * Copyright 2021 Google LLC
506
- *
507
- * Licensed under the Apache License, Version 2.0 (the "License");
508
- * you may not use this file except in compliance with the License.
509
- * You may obtain a copy of the License at
510
- *
511
- * http://www.apache.org/licenses/LICENSE-2.0
512
- *
513
- * Unless required by applicable law or agreed to in writing, software
514
- * distributed under the License is distributed on an "AS IS" BASIS,
515
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
516
- * See the License for the specific language governing permissions and
517
- * limitations under the License.
518
- */
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'
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;
560
- }
561
- /**
562
- * Assert the promise to be rejected with a "permission denied" error.
563
- *
564
- * Useful to assert a certain request to be denied by Security Rules. See example below.
565
- * This function recognizes permission-denied errors from Database, Firestore, and Storage JS SDKs.
566
- *
567
- * @param pr - the promise to be asserted
568
- * @returns a Promise that is fulfilled if pr is rejected with "permission denied". If pr is
569
- * rejected with any other error or resolved, the returned promise rejects.
570
- * @public
571
- * @example
572
- * ```javascript
573
- * const unauthed = testEnv.unauthenticatedContext();
574
- * await assertFails(get(doc(unauthed.firestore(), '/private/doc'), { ... });
575
- * ```
576
- */
577
- function assertFails(pr) {
578
- return pr.then(() => {
579
- return Promise.reject(new Error('Expected request to fail, but it succeeded.'));
580
- }, (err) => {
581
- var _a, _b;
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' ||
585
- errCode === 'permission_denied' ||
586
- errMessage.indexOf('permission_denied') >= 0 ||
587
- errMessage.indexOf('permission denied') >= 0 ||
588
- // Storage permission errors contain message: (storage/unauthorized)
589
- errMessage.indexOf('unauthorized') >= 0;
590
- if (!isPermissionDenied) {
591
- return Promise.reject(new Error(`Expected PERMISSION_DENIED but got unexpected error: ${err}`));
592
- }
593
- return err;
594
- });
595
- }
596
- /**
597
- * Assert the promise to be successful.
598
- *
599
- * This is a no-op function returning the passed promise as-is, but can be used for documentational
600
- * purposes in test code to emphasize that a certain request should succeed (e.g. allowed by rules).
601
- *
602
- * @public
603
- * @example
604
- * ```javascript
605
- * const alice = testEnv.authenticatedContext('alice');
606
- * await assertSucceeds(get(doc(alice.firestore(), '/doc/readable/by/alice'), { ... });
607
- * ```
608
- */
609
- function assertSucceeds(pr) {
610
- return pr;
503
+ /**
504
+ * @license
505
+ * Copyright 2021 Google LLC
506
+ *
507
+ * Licensed under the Apache License, Version 2.0 (the "License");
508
+ * you may not use this file except in compliance with the License.
509
+ * You may obtain a copy of the License at
510
+ *
511
+ * http://www.apache.org/licenses/LICENSE-2.0
512
+ *
513
+ * Unless required by applicable law or agreed to in writing, software
514
+ * distributed under the License is distributed on an "AS IS" BASIS,
515
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
516
+ * See the License for the specific language governing permissions and
517
+ * limitations under the License.
518
+ */
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'
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;
560
+ }
561
+ /**
562
+ * Assert the promise to be rejected with a "permission denied" error.
563
+ *
564
+ * Useful to assert a certain request to be denied by Security Rules. See example below.
565
+ * This function recognizes permission-denied errors from Database, Firestore, and Storage JS SDKs.
566
+ *
567
+ * @param pr - the promise to be asserted
568
+ * @returns a Promise that is fulfilled if pr is rejected with "permission denied". If pr is
569
+ * rejected with any other error or resolved, the returned promise rejects.
570
+ * @public
571
+ * @example
572
+ * ```javascript
573
+ * const unauthed = testEnv.unauthenticatedContext();
574
+ * await assertFails(get(doc(unauthed.firestore(), '/private/doc'), { ... });
575
+ * ```
576
+ */
577
+ function assertFails(pr) {
578
+ return pr.then(() => {
579
+ return Promise.reject(new Error('Expected request to fail, but it succeeded.'));
580
+ }, (err) => {
581
+ var _a, _b;
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' ||
585
+ errCode === 'permission_denied' ||
586
+ errMessage.indexOf('permission_denied') >= 0 ||
587
+ errMessage.indexOf('permission denied') >= 0 ||
588
+ // Storage permission errors contain message: (storage/unauthorized)
589
+ errMessage.indexOf('unauthorized') >= 0;
590
+ if (!isPermissionDenied) {
591
+ return Promise.reject(new Error(`Expected PERMISSION_DENIED but got unexpected error: ${err}`));
592
+ }
593
+ return err;
594
+ });
595
+ }
596
+ /**
597
+ * Assert the promise to be successful.
598
+ *
599
+ * This is a no-op function returning the passed promise as-is, but can be used for documentational
600
+ * purposes in test code to emphasize that a certain request should succeed (e.g. allowed by rules).
601
+ *
602
+ * @public
603
+ * @example
604
+ * ```javascript
605
+ * const alice = testEnv.authenticatedContext('alice');
606
+ * await assertSucceeds(get(doc(alice.firestore(), '/doc/readable/by/alice'), { ... });
607
+ * ```
608
+ */
609
+ function assertSucceeds(pr) {
610
+ return pr;
611
611
  }
612
612
 
613
613
  exports.assertFails = assertFails;