@dereekb/firebase 13.0.5 → 13.0.6

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/test/index.cjs.js CHANGED
@@ -819,8 +819,8 @@ function describeFirestoreAccessorDriverTests(f) {
819
819
  let mockItemFirestoreDocumentAccessor;
820
820
  let items;
821
821
  beforeEach(async () => {
822
- mockItemFirestoreDocumentAccessor = f.instance.firestoreCollection.documentAccessor();
823
- items = await firebase.makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
822
+ mockItemFirestoreDocumentAccessor = f.instance.mockItemCollection.documentAccessor();
823
+ items = await firebase.makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
824
824
  count: testDocumentCount,
825
825
  init: (i) => {
826
826
  return {
@@ -845,8 +845,8 @@ function describeFirestoreAccessorDriverTests(f) {
845
845
  dataForUpdate: () => ({ test: false }),
846
846
  hasRemainingDataFromFirstOfTwoUpdate: (data) => (data.tags?.length || 0) > 0 && data.tags?.[0] === 'a',
847
847
  hasDataFromUpdate: (data) => data.test === false,
848
- loadDocumentForTransaction: (transaction, ref) => f.instance.firestoreCollection.documentAccessorForTransaction(transaction).loadDocument(ref),
849
- loadDocumentForWriteBatch: (writeBatch, ref) => f.instance.firestoreCollection.documentAccessorForWriteBatch(writeBatch).loadDocument(ref)
848
+ loadDocumentForTransaction: (transaction, ref) => f.instance.mockItemCollection.documentAccessorForTransaction(transaction).loadDocument(ref),
849
+ loadDocumentForWriteBatch: (writeBatch, ref) => f.instance.mockItemCollection.documentAccessorForWriteBatch(writeBatch).loadDocument(ref)
850
850
  }));
851
851
  describe('increment()', () => {
852
852
  it(`should increase the item's value`, async () => {
@@ -887,7 +887,7 @@ function describeFirestoreAccessorDriverTests(f) {
887
887
  it(`should increase the item's value`, async () => {
888
888
  const update = { number: 3 };
889
889
  await f.parent.firestoreContext.runTransaction(async (transaction) => {
890
- const itemDocumentInTransaction = await f.instance.firestoreCollection.documentAccessorForTransaction(transaction).loadDocumentForId(itemDocument.id);
890
+ const itemDocumentInTransaction = await f.instance.mockItemCollection.documentAccessorForTransaction(transaction).loadDocumentForId(itemDocument.id);
891
891
  const data = await itemDocumentInTransaction.snapshotData();
892
892
  expect(data?.number).toBe(undefined);
893
893
  await itemDocumentInTransaction.increment(update);
@@ -900,7 +900,7 @@ function describeFirestoreAccessorDriverTests(f) {
900
900
  it(`should increase the item's value`, async () => {
901
901
  const update = { number: 3 };
902
902
  const writeBatch = f.parent.firestoreContext.batch();
903
- const itemDocumentForWriteBatch = await f.instance.firestoreCollection.documentAccessorForWriteBatch(writeBatch).loadDocumentForId(itemDocument.id);
903
+ const itemDocumentForWriteBatch = await f.instance.mockItemCollection.documentAccessorForWriteBatch(writeBatch).loadDocumentForId(itemDocument.id);
904
904
  await itemDocumentForWriteBatch.increment(update);
905
905
  await writeBatch.commit();
906
906
  const result = await itemDocument.snapshotData();
@@ -1577,7 +1577,7 @@ function describeFirestoreQueryDriverTests(f) {
1577
1577
  const EVEN_TAG = 'even';
1578
1578
  const ODD_TAG = 'odd';
1579
1579
  beforeEach(async () => {
1580
- items = await firebase.makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
1580
+ items = await firebase.makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
1581
1581
  count: testDocumentCount,
1582
1582
  init: (i) => {
1583
1583
  return {
@@ -2166,7 +2166,7 @@ function describeFirestoreQueryDriverTests(f) {
2166
2166
  describe('queryDocument', () => {
2167
2167
  let queryDocument;
2168
2168
  beforeEach(async () => {
2169
- queryDocument = f.instance.firestoreCollection.queryDocument;
2169
+ queryDocument = f.instance.mockItemCollection.queryDocument;
2170
2170
  });
2171
2171
  describe('filter()', () => {
2172
2172
  it('should apply the filter to the query', async () => {
@@ -2270,7 +2270,7 @@ function describeFirestoreQueryDriverTests(f) {
2270
2270
  tryComplete();
2271
2271
  });
2272
2272
  // add one item
2273
- util.waitForMs(10).then(() => firebase.makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
2273
+ util.waitForMs(10).then(() => firebase.makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2274
2274
  count: itemsToAdd,
2275
2275
  init: (i) => {
2276
2276
  return {
@@ -2346,7 +2346,7 @@ function describeFirestoreQueryDriverTests(f) {
2346
2346
  tryComplete();
2347
2347
  });
2348
2348
  // add one item
2349
- util.waitForMs(10).then(() => firebase.makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
2349
+ util.waitForMs(10).then(() => firebase.makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2350
2350
  count: itemsToAdd,
2351
2351
  init: (i) => {
2352
2352
  return {
@@ -2390,7 +2390,7 @@ function describeFirestoreQueryDriverTests(f) {
2390
2390
  describe('query', () => {
2391
2391
  let query;
2392
2392
  beforeEach(async () => {
2393
- query = f.instance.firestoreCollection.query;
2393
+ query = f.instance.mockItemCollection.query;
2394
2394
  });
2395
2395
  describe('streamDocs()', () => {
2396
2396
  let sub;
@@ -2418,7 +2418,7 @@ function describeFirestoreQueryDriverTests(f) {
2418
2418
  tryComplete();
2419
2419
  });
2420
2420
  // add one item
2421
- util.waitForMs(10).then(() => firebase.makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
2421
+ util.waitForMs(10).then(() => firebase.makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2422
2422
  count: itemsToAdd,
2423
2423
  init: (i) => {
2424
2424
  return {
@@ -2643,7 +2643,7 @@ function describeFirestoreQueryDriverTests(f) {
2643
2643
  const oddPrefix = mockItemIdentity.collectionType + 'd' + '/'; // similar, but not quite the same
2644
2644
  const expectedNumberOfEvenValues = Math.ceil(testDocumentCount / 2);
2645
2645
  beforeEach(async () => {
2646
- items = await firebase.makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
2646
+ items = await firebase.makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2647
2647
  count: testDocumentCount,
2648
2648
  init: (i) => {
2649
2649
  const isEven = util.isEvenNumber(i);
@@ -2911,6 +2911,733 @@ function describeFirestoreQueryDriverTests(f) {
2911
2911
  });
2912
2912
  }
2913
2913
 
2914
+ /**
2915
+ * Describes utility driver tests, using a MockItemCollectionFixture.
2916
+ *
2917
+ * @param f
2918
+ */
2919
+ function describeFirestoreDocumentUtilityTests(f) {
2920
+ describe('FirestoreDocumentUtilities', () => {
2921
+ const testDocumentCount = 5;
2922
+ let items;
2923
+ const startDate = dateFns.addDays(dateFns.startOfDay(new Date()), 1);
2924
+ const EVEN_TAG = 'even';
2925
+ const ODD_TAG = 'odd';
2926
+ beforeEach(async () => {
2927
+ items = await firebase.makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2928
+ count: testDocumentCount,
2929
+ init: (i) => {
2930
+ return {
2931
+ value: `${i}`,
2932
+ number: i,
2933
+ date: dateFns.addHours(startDate, i),
2934
+ tags: [`${i}`, `${util.isEvenNumber(i) ? EVEN_TAG : ODD_TAG}`],
2935
+ test: true
2936
+ };
2937
+ }
2938
+ });
2939
+ });
2940
+ let sub;
2941
+ beforeEach(() => {
2942
+ sub = new rxjs.SubscriptionObject();
2943
+ });
2944
+ afterEach(() => {
2945
+ sub.destroy();
2946
+ });
2947
+ describe('query.util.ts', () => {
2948
+ // MARK: streamFromOnSnapshot
2949
+ describe('streamFromOnSnapshot()', () => {
2950
+ it('should call unsubscribe when the subscriber unsubscribes', () => {
2951
+ let unsubscribeCalled = false;
2952
+ const obs = firebase.streamFromOnSnapshot(({ next }) => {
2953
+ next('value');
2954
+ return () => {
2955
+ unsubscribeCalled = true;
2956
+ };
2957
+ });
2958
+ const subscription = obs.subscribe();
2959
+ expect(unsubscribeCalled).toBe(false);
2960
+ subscription.unsubscribe();
2961
+ expect(unsubscribeCalled).toBe(true);
2962
+ });
2963
+ it('should unsubscribe the onSnapshot listener when first() completes', () => {
2964
+ let unsubscribeCalled = false;
2965
+ const obs = firebase.streamFromOnSnapshot(({ next }) => {
2966
+ next('value');
2967
+ return () => {
2968
+ unsubscribeCalled = true;
2969
+ };
2970
+ });
2971
+ // first() completes the subscriber, then RxJS tears down the source
2972
+ const subscription = obs.pipe(rxjs$1.first()).subscribe();
2973
+ // After first() + subscribe complete synchronously, teardown should have run
2974
+ expect(subscription.closed).toBe(true);
2975
+ expect(unsubscribeCalled).toBe(true);
2976
+ });
2977
+ it('should unsubscribe onSnapshot listeners from document streams after subscriber unsubscribes', test.callbackTest((done) => {
2978
+ sub.subscription = firebase.latestSnapshotsFromDocuments(items)
2979
+ .pipe(rxjs$1.first())
2980
+ .subscribe({
2981
+ complete: () => {
2982
+ // With refCount: true on shareReplay, unsubscribing the last subscriber
2983
+ // tears down the source, which calls unsubscribe on each onSnapshot listener.
2984
+ // If this test completes without the "active listeners" error, the cleanup worked.
2985
+ done();
2986
+ }
2987
+ });
2988
+ }));
2989
+ });
2990
+ });
2991
+ describe('document.utility.ts', () => {
2992
+ // MARK: newDocuments
2993
+ describe('newDocuments()', () => {
2994
+ it('should create the specified number of document instances', () => {
2995
+ const count = 3;
2996
+ const docs = firebase.newDocuments(f.instance.mockItemCollection.documentAccessor(), count);
2997
+ expect(docs.length).toBe(count);
2998
+ });
2999
+ it('should create documents with unique IDs', () => {
3000
+ const docs = firebase.newDocuments(f.instance.mockItemCollection.documentAccessor(), 3);
3001
+ const ids = docs.map((x) => x.id);
3002
+ expect(util.unique(ids).length).toBe(3);
3003
+ });
3004
+ it('should not persist documents to Firestore', async () => {
3005
+ const docs = firebase.newDocuments(f.instance.mockItemCollection.documentAccessor(), 2);
3006
+ for (const doc of docs) {
3007
+ const exists = await doc.exists();
3008
+ expect(exists).toBe(false);
3009
+ }
3010
+ });
3011
+ });
3012
+ // MARK: makeDocuments
3013
+ describe('makeDocuments()', () => {
3014
+ it('should create and persist documents in Firestore', async () => {
3015
+ const count = 3;
3016
+ const docs = await firebase.makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
3017
+ count,
3018
+ init: (i) => ({ value: `test_${i}`, test: true })
3019
+ });
3020
+ expect(docs.length).toBe(count);
3021
+ for (const doc of docs) {
3022
+ const exists = await doc.exists();
3023
+ expect(exists).toBe(true);
3024
+ }
3025
+ });
3026
+ it('should not persist documents when init returns null', async () => {
3027
+ const docs = await firebase.makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
3028
+ count: 2,
3029
+ init: () => null
3030
+ });
3031
+ expect(docs.length).toBe(2);
3032
+ for (const doc of docs) {
3033
+ const exists = await doc.exists();
3034
+ expect(exists).toBe(false);
3035
+ }
3036
+ });
3037
+ it('should use custom newDocument factory when provided', async () => {
3038
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3039
+ const customId = 'custom_doc_id';
3040
+ const docs = await firebase.makeDocuments(accessor, {
3041
+ count: 1,
3042
+ newDocument: (acc) => acc.loadDocumentForId(customId),
3043
+ init: () => ({ value: 'custom', test: true })
3044
+ });
3045
+ expect(docs.length).toBe(1);
3046
+ expect(docs[0].id).toBe(customId);
3047
+ const exists = await docs[0].exists();
3048
+ expect(exists).toBe(true);
3049
+ });
3050
+ });
3051
+ // MARK: getDocumentSnapshots
3052
+ describe('getDocumentSnapshots()', () => {
3053
+ it('should return snapshots for all documents', async () => {
3054
+ const snapshots = await firebase.getDocumentSnapshots(items);
3055
+ expect(snapshots.length).toBe(testDocumentCount);
3056
+ snapshots.forEach((snapshot) => {
3057
+ expect(snapshot.data()).toBeDefined();
3058
+ });
3059
+ });
3060
+ it('should preserve ordering of the input array', async () => {
3061
+ const snapshots = await firebase.getDocumentSnapshots(items);
3062
+ for (let i = 0; i < items.length; i++) {
3063
+ expect(snapshots[i].id).toBe(items[i].id);
3064
+ }
3065
+ });
3066
+ });
3067
+ // MARK: getDocumentSnapshotPair
3068
+ describe('getDocumentSnapshotPair()', () => {
3069
+ it('should return a pair with both document and snapshot', async () => {
3070
+ const pair = await firebase.getDocumentSnapshotPair(items[0]);
3071
+ expect(pair.document).toBe(items[0]);
3072
+ expect(pair.snapshot).toBeDefined();
3073
+ expect(pair.snapshot.data()).toBeDefined();
3074
+ expect(pair.snapshot.id).toBe(items[0].id);
3075
+ });
3076
+ });
3077
+ // MARK: getDocumentSnapshotPairs
3078
+ describe('getDocumentSnapshotPairs()', () => {
3079
+ it('should return pairs for all documents', async () => {
3080
+ const pairs = await firebase.getDocumentSnapshotPairs(items);
3081
+ expect(pairs.length).toBe(testDocumentCount);
3082
+ pairs.forEach((pair, i) => {
3083
+ expect(pair.document).toBe(items[i]);
3084
+ expect(pair.snapshot.data()).toBeDefined();
3085
+ });
3086
+ });
3087
+ });
3088
+ // MARK: getDocumentSnapshotDataPair
3089
+ describe('getDocumentSnapshotDataPair()', () => {
3090
+ it('should return a triplet with document, snapshot, and data with id/key', async () => {
3091
+ const pair = await firebase.getDocumentSnapshotDataPair(items[0]);
3092
+ expect(pair.document).toBe(items[0]);
3093
+ expect(pair.snapshot).toBeDefined();
3094
+ expect(pair.data).toBeDefined();
3095
+ expect(pair.data.id).toBe(items[0].id);
3096
+ expect(pair.data.key).toBe(items[0].key);
3097
+ expect(pair.data.test).toBe(true);
3098
+ });
3099
+ it('should return undefined data for a non-existent document', async () => {
3100
+ const newDoc = firebase.newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3101
+ const pair = await firebase.getDocumentSnapshotDataPair(newDoc);
3102
+ expect(pair.document).toBe(newDoc);
3103
+ expect(pair.data).toBeUndefined();
3104
+ });
3105
+ });
3106
+ // MARK: getDocumentSnapshotDataPairs
3107
+ describe('getDocumentSnapshotDataPairs()', () => {
3108
+ it('should return triplets for all documents', async () => {
3109
+ const pairs = await firebase.getDocumentSnapshotDataPairs(items);
3110
+ expect(pairs.length).toBe(testDocumentCount);
3111
+ pairs.forEach((pair, i) => {
3112
+ expect(pair.document).toBe(items[i]);
3113
+ expect(pair.data).toBeDefined();
3114
+ expect(pair.data.id).toBe(items[i].id);
3115
+ expect(pair.data.key).toBe(items[i].key);
3116
+ });
3117
+ });
3118
+ });
3119
+ // MARK: getDocumentSnapshotDataPairsWithData
3120
+ describe('getDocumentSnapshotDataPairsWithData()', () => {
3121
+ it('should return triplets for all existing documents', async () => {
3122
+ const pairs = await firebase.getDocumentSnapshotDataPairsWithData(items);
3123
+ expect(pairs.length).toBe(testDocumentCount);
3124
+ pairs.forEach((pair) => {
3125
+ expect(pair.data).toBeDefined();
3126
+ expect(pair.data.id).toBeDefined();
3127
+ expect(pair.data.key).toBeDefined();
3128
+ });
3129
+ });
3130
+ it('should filter out non-existent documents', async () => {
3131
+ const newDoc = firebase.newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3132
+ const allDocs = [...items, newDoc];
3133
+ const pairs = await firebase.getDocumentSnapshotDataPairsWithData(allDocs);
3134
+ expect(pairs.length).toBe(testDocumentCount);
3135
+ expect(pairs.every((p) => p.data != null)).toBe(true);
3136
+ });
3137
+ });
3138
+ // MARK: getDocumentSnapshotDataTuples
3139
+ describe('getDocumentSnapshotDataTuples()', () => {
3140
+ it('should return [document, data] tuples for all documents', async () => {
3141
+ const tuples = await firebase.getDocumentSnapshotDataTuples(items);
3142
+ expect(tuples.length).toBe(testDocumentCount);
3143
+ tuples.forEach((tuple, i) => {
3144
+ expect(tuple[0]).toBe(items[i]);
3145
+ expect(tuple[1]).toBeDefined();
3146
+ expect(tuple[1].test).toBe(true);
3147
+ });
3148
+ });
3149
+ it('should return undefined data for non-existent documents', async () => {
3150
+ const newDoc = firebase.newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3151
+ const tuples = await firebase.getDocumentSnapshotDataTuples([newDoc]);
3152
+ expect(tuples.length).toBe(1);
3153
+ expect(tuples[0][0]).toBe(newDoc);
3154
+ expect(tuples[0][1]).toBeUndefined();
3155
+ });
3156
+ });
3157
+ // MARK: getDocumentSnapshotData
3158
+ describe('getDocumentSnapshotData()', () => {
3159
+ it('should return data with id and key by default', async () => {
3160
+ const data = await firebase.getDocumentSnapshotData(items[0]);
3161
+ expect(data).toBeDefined();
3162
+ expect(data.id).toBe(items[0].id);
3163
+ expect(data.key).toBe(items[0].key);
3164
+ expect(data.test).toBe(true);
3165
+ });
3166
+ it('should return data with id and key when withId is true', async () => {
3167
+ const data = await firebase.getDocumentSnapshotData(items[0], true);
3168
+ expect(data).toBeDefined();
3169
+ expect(data.id).toBe(items[0].id);
3170
+ expect(data.key).toBe(items[0].key);
3171
+ });
3172
+ it('should return raw data without id/key when withId is false', async () => {
3173
+ const data = await firebase.getDocumentSnapshotData(items[0], false);
3174
+ expect(data).toBeDefined();
3175
+ expect(data.test).toBe(true);
3176
+ expect(data.id).toBeUndefined();
3177
+ expect(data.key).toBeUndefined();
3178
+ });
3179
+ it('should return undefined for a non-existent document', async () => {
3180
+ const newDoc = firebase.newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3181
+ const data = await firebase.getDocumentSnapshotData(newDoc);
3182
+ expect(data).toBeUndefined();
3183
+ });
3184
+ });
3185
+ // MARK: getDocumentSnapshotsData
3186
+ describe('getDocumentSnapshotsData()', () => {
3187
+ it('should return data for all existing documents with id/key by default', async () => {
3188
+ const data = await firebase.getDocumentSnapshotsData(items);
3189
+ expect(data.length).toBe(testDocumentCount);
3190
+ data.forEach((d) => {
3191
+ expect(d.id).toBeDefined();
3192
+ expect(d.key).toBeDefined();
3193
+ expect(d.test).toBe(true);
3194
+ });
3195
+ });
3196
+ it('should return raw data without id/key when withId is false', async () => {
3197
+ const data = await firebase.getDocumentSnapshotsData(items, false);
3198
+ expect(data.length).toBe(testDocumentCount);
3199
+ data.forEach((d) => {
3200
+ expect(d.test).toBe(true);
3201
+ });
3202
+ });
3203
+ });
3204
+ // MARK: getDataFromDocumentSnapshots
3205
+ describe('getDataFromDocumentSnapshots()', () => {
3206
+ it('should extract data with id/key from snapshots', async () => {
3207
+ const snapshots = await firebase.getDocumentSnapshots(items);
3208
+ const data = firebase.getDataFromDocumentSnapshots(snapshots);
3209
+ expect(data.length).toBe(testDocumentCount);
3210
+ data.forEach((d) => {
3211
+ expect(d.id).toBeDefined();
3212
+ expect(d.key).toBeDefined();
3213
+ });
3214
+ });
3215
+ it('should extract raw data when withId is false', async () => {
3216
+ const snapshots = await firebase.getDocumentSnapshots(items);
3217
+ const data = firebase.getDataFromDocumentSnapshots(snapshots, false);
3218
+ expect(data.length).toBe(testDocumentCount);
3219
+ });
3220
+ it('should filter out non-existent document snapshots', async () => {
3221
+ const newDoc = firebase.newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3222
+ const allDocs = [...items, newDoc];
3223
+ const snapshots = await firebase.getDocumentSnapshots(allDocs);
3224
+ const data = firebase.getDataFromDocumentSnapshots(snapshots);
3225
+ expect(data.length).toBe(testDocumentCount);
3226
+ });
3227
+ });
3228
+ // MARK: loadDocumentsForSnapshots
3229
+ describe('loadDocumentsForSnapshots()', () => {
3230
+ it('should load documents from a query snapshot', async () => {
3231
+ const querySnapshot = await f.instance.mockItemCollection.query(firebase.where('test', '==', true)).getDocs();
3232
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3233
+ const docs = firebase.loadDocumentsForSnapshots(accessor, querySnapshot);
3234
+ expect(docs.length).toBe(testDocumentCount);
3235
+ docs.forEach((doc) => {
3236
+ expect(doc).toBeDefined();
3237
+ expect(doc.id).toBeDefined();
3238
+ });
3239
+ });
3240
+ });
3241
+ // MARK: loadDocumentsForDocumentReferences
3242
+ describe('loadDocumentsForDocumentReferences()', () => {
3243
+ it('should load documents from references', () => {
3244
+ const refs = items.map((x) => x.documentRef);
3245
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3246
+ const docs = firebase.loadDocumentsForDocumentReferences(accessor, refs);
3247
+ expect(docs.length).toBe(testDocumentCount);
3248
+ docs.forEach((doc, i) => {
3249
+ expect(doc.id).toBe(items[i].id);
3250
+ });
3251
+ });
3252
+ });
3253
+ // MARK: loadDocumentsForDocumentReferencesFromValues
3254
+ describe('loadDocumentsForDocumentReferencesFromValues()', () => {
3255
+ it('should extract refs from values and load documents', () => {
3256
+ const values = items.map((x) => ({ ref: x.documentRef }));
3257
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3258
+ const docs = firebase.loadDocumentsForDocumentReferencesFromValues(accessor, values, (v) => v.ref);
3259
+ expect(docs.length).toBe(testDocumentCount);
3260
+ docs.forEach((doc, i) => {
3261
+ expect(doc.id).toBe(items[i].id);
3262
+ });
3263
+ });
3264
+ });
3265
+ // MARK: loadDocumentsForKeys
3266
+ describe('loadDocumentsForKeys()', () => {
3267
+ it('should load documents from keys', () => {
3268
+ const keys = items.map((x) => x.key);
3269
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3270
+ const docs = firebase.loadDocumentsForKeys(accessor, keys);
3271
+ expect(docs.length).toBe(testDocumentCount);
3272
+ docs.forEach((doc, i) => {
3273
+ expect(doc.key).toBe(items[i].key);
3274
+ });
3275
+ });
3276
+ });
3277
+ // MARK: loadDocumentsForKeysFromValues
3278
+ describe('loadDocumentsForKeysFromValues()', () => {
3279
+ it('should extract keys from values and load documents', () => {
3280
+ const values = items.map((x) => ({ k: x.key }));
3281
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3282
+ const docs = firebase.loadDocumentsForKeysFromValues(accessor, values, (v) => v.k);
3283
+ expect(docs.length).toBe(testDocumentCount);
3284
+ docs.forEach((doc, i) => {
3285
+ expect(doc.key).toBe(items[i].key);
3286
+ });
3287
+ });
3288
+ });
3289
+ // MARK: loadDocumentsForIds
3290
+ describe('loadDocumentsForIds()', () => {
3291
+ it('should load documents from IDs', () => {
3292
+ const ids = items.map((x) => x.id);
3293
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3294
+ const docs = firebase.loadDocumentsForIds(accessor, ids);
3295
+ expect(docs.length).toBe(testDocumentCount);
3296
+ docs.forEach((doc, i) => {
3297
+ expect(doc.id).toBe(items[i].id);
3298
+ });
3299
+ });
3300
+ });
3301
+ // MARK: loadDocumentsForIdsFromValues
3302
+ describe('loadDocumentsForIdsFromValues()', () => {
3303
+ it('should extract IDs from values and load documents', () => {
3304
+ const values = items.map((x) => ({ docId: x.id }));
3305
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3306
+ const docs = firebase.loadDocumentsForIdsFromValues(accessor, values, (v) => v.docId);
3307
+ expect(docs.length).toBe(testDocumentCount);
3308
+ docs.forEach((doc, i) => {
3309
+ expect(doc.id).toBe(items[i].id);
3310
+ });
3311
+ });
3312
+ });
3313
+ // MARK: firestoreDocumentLoader
3314
+ describe('firestoreDocumentLoader()', () => {
3315
+ it('should create a loader that loads documents from references', () => {
3316
+ const loader = firebase.firestoreDocumentLoader(f.instance.mockItemCollection);
3317
+ const refs = items.map((x) => x.documentRef);
3318
+ const docs = loader(refs);
3319
+ expect(docs.length).toBe(testDocumentCount);
3320
+ docs.forEach((doc, i) => {
3321
+ expect(doc.id).toBe(items[i].id);
3322
+ });
3323
+ });
3324
+ });
3325
+ // MARK: firestoreDocumentSnapshotPairsLoader
3326
+ describe('firestoreDocumentSnapshotPairsLoader()', () => {
3327
+ it('should create a loader that converts snapshots to data pairs', async () => {
3328
+ const loader = firebase.firestoreDocumentSnapshotPairsLoader(f.instance.mockItemCollection);
3329
+ const snapshots = await firebase.getDocumentSnapshots(items);
3330
+ const pairs = loader(snapshots);
3331
+ expect(pairs.length).toBe(testDocumentCount);
3332
+ pairs.forEach((pair) => {
3333
+ expect(pair.document).toBeDefined();
3334
+ expect(pair.snapshot).toBeDefined();
3335
+ expect(pair.data).toBeDefined();
3336
+ expect(pair.data.id).toBeDefined();
3337
+ expect(pair.data.key).toBeDefined();
3338
+ });
3339
+ });
3340
+ });
3341
+ // MARK: documentData
3342
+ describe('documentData()', () => {
3343
+ it('should return data with id/key when withId is true', async () => {
3344
+ const snapshot = await items[0].accessor.get();
3345
+ const data = firebase.documentData(snapshot, true);
3346
+ expect(data).toBeDefined();
3347
+ expect(data.id).toBe(items[0].id);
3348
+ expect(data.key).toBe(items[0].key);
3349
+ });
3350
+ it('should return raw data when withId is false', async () => {
3351
+ const snapshot = await items[0].accessor.get();
3352
+ const data = firebase.documentData(snapshot, false);
3353
+ expect(data).toBeDefined();
3354
+ expect(data.test).toBe(true);
3355
+ });
3356
+ });
3357
+ // MARK: documentDataFunction
3358
+ describe('documentDataFunction()', () => {
3359
+ it('should return a function that extracts data with id/key when withId is true', async () => {
3360
+ const fn = firebase.documentDataFunction(true);
3361
+ const snapshot = await items[0].accessor.get();
3362
+ const data = fn(snapshot);
3363
+ expect(data).toBeDefined();
3364
+ expect(data.id).toBe(items[0].id);
3365
+ expect(data.key).toBe(items[0].key);
3366
+ });
3367
+ it('should return a function that extracts raw data when withId is false', async () => {
3368
+ const fn = firebase.documentDataFunction(false);
3369
+ const snapshot = await items[0].accessor.get();
3370
+ const data = fn(snapshot);
3371
+ expect(data).toBeDefined();
3372
+ expect(data.id).toBeUndefined();
3373
+ });
3374
+ });
3375
+ // MARK: documentDataWithIdAndKey
3376
+ describe('documentDataWithIdAndKey()', () => {
3377
+ it('should extract data with id and key from a snapshot', async () => {
3378
+ const snapshot = await items[0].accessor.get();
3379
+ const data = firebase.documentDataWithIdAndKey(snapshot);
3380
+ expect(data).toBeDefined();
3381
+ expect(data.id).toBe(items[0].id);
3382
+ expect(data.key).toBe(items[0].key);
3383
+ expect(data.test).toBe(true);
3384
+ });
3385
+ it('should return undefined for a non-existent document', async () => {
3386
+ const newDoc = firebase.newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3387
+ const snapshot = await newDoc.accessor.get();
3388
+ const data = firebase.documentDataWithIdAndKey(snapshot);
3389
+ expect(data).toBeUndefined();
3390
+ });
3391
+ });
3392
+ // MARK: setIdAndKeyFromSnapshotOnDocumentData
3393
+ describe('setIdAndKeyFromSnapshotOnDocumentData()', () => {
3394
+ it('should mutate data in-place to add id and key from snapshot', async () => {
3395
+ const snapshot = await items[0].accessor.get();
3396
+ const rawData = { test: true };
3397
+ const result = firebase.setIdAndKeyFromSnapshotOnDocumentData(rawData, snapshot);
3398
+ expect(result.id).toBe(items[0].id);
3399
+ expect(result.key).toBe(items[0].key);
3400
+ expect(result).toBe(rawData); // same reference
3401
+ });
3402
+ });
3403
+ // MARK: setIdAndKeyFromKeyIdRefOnDocumentData
3404
+ describe('setIdAndKeyFromKeyIdRefOnDocumentData()', () => {
3405
+ it('should mutate data in-place to add id and key from model ref', () => {
3406
+ const rawData = { test: true };
3407
+ const modelRef = { id: items[0].id, key: items[0].key };
3408
+ const result = firebase.setIdAndKeyFromKeyIdRefOnDocumentData(rawData, modelRef);
3409
+ expect(result.id).toBe(items[0].id);
3410
+ expect(result.key).toBe(items[0].key);
3411
+ expect(result).toBe(rawData);
3412
+ });
3413
+ });
3414
+ // MARK: useDocumentSnapshot
3415
+ describe('useDocumentSnapshot()', () => {
3416
+ it('should fetch snapshot and pass it to the use callback', async () => {
3417
+ const result = await firebase.useDocumentSnapshot(items[0], (snapshot) => {
3418
+ expect(snapshot.data()).toBeDefined();
3419
+ return snapshot.id;
3420
+ });
3421
+ expect(result).toBe(items[0].id);
3422
+ });
3423
+ it('should return default value when document is null', async () => {
3424
+ const result = await firebase.useDocumentSnapshot(null, () => 'used', 'default');
3425
+ expect(result).toBe('default');
3426
+ });
3427
+ });
3428
+ // MARK: useDocumentSnapshotData
3429
+ describe('useDocumentSnapshotData()', () => {
3430
+ it('should fetch snapshot data and pass it to the use callback', async () => {
3431
+ const result = await firebase.useDocumentSnapshotData(items[0], (data) => {
3432
+ expect(data.test).toBe(true);
3433
+ return data.value;
3434
+ });
3435
+ expect(result).toBe('0');
3436
+ });
3437
+ it('should return default value when document is null', async () => {
3438
+ const result = await firebase.useDocumentSnapshotData(null, () => 'used', 'default');
3439
+ expect(result).toBe('default');
3440
+ });
3441
+ });
3442
+ // MARK: Key Accessors
3443
+ describe('firestoreModelIdFromDocument()', () => {
3444
+ it('should return the document ID', () => {
3445
+ const id = firebase.firestoreModelIdFromDocument(items[0]);
3446
+ expect(id).toBe(items[0].id);
3447
+ });
3448
+ });
3449
+ describe('firestoreModelIdsFromDocuments()', () => {
3450
+ it('should return IDs for all documents', () => {
3451
+ const ids = firebase.firestoreModelIdsFromDocuments(items);
3452
+ expect(ids.length).toBe(testDocumentCount);
3453
+ ids.forEach((id, i) => {
3454
+ expect(id).toBe(items[i].id);
3455
+ });
3456
+ });
3457
+ });
3458
+ describe('firestoreModelKeyFromDocument()', () => {
3459
+ it('should return the full Firestore path', () => {
3460
+ const key = firebase.firestoreModelKeyFromDocument(items[0]);
3461
+ expect(key).toBe(items[0].key);
3462
+ });
3463
+ });
3464
+ describe('firestoreModelKeysFromDocuments()', () => {
3465
+ it('should return keys for all documents', () => {
3466
+ const keys = firebase.firestoreModelKeysFromDocuments(items);
3467
+ expect(keys.length).toBe(testDocumentCount);
3468
+ keys.forEach((key, i) => {
3469
+ expect(key).toBe(items[i].key);
3470
+ });
3471
+ });
3472
+ });
3473
+ describe('documentReferenceFromDocument()', () => {
3474
+ it('should return the document reference', () => {
3475
+ const ref = firebase.documentReferenceFromDocument(items[0]);
3476
+ expect(ref).toBe(items[0].documentRef);
3477
+ });
3478
+ });
3479
+ describe('documentReferencesFromDocuments()', () => {
3480
+ it('should return references for all documents', () => {
3481
+ const refs = firebase.documentReferencesFromDocuments(items);
3482
+ expect(refs.length).toBe(testDocumentCount);
3483
+ refs.forEach((ref, i) => {
3484
+ expect(ref).toBe(items[i].documentRef);
3485
+ });
3486
+ });
3487
+ });
3488
+ });
3489
+ describe('document.rxjs.ts', () => {
3490
+ // MARK: latestSnapshotsFromDocuments
3491
+ describe('latestSnapshotsFromDocuments()', () => {
3492
+ it('should emit snapshots for all documents', test.callbackTest((done) => {
3493
+ sub.subscription = firebase.latestSnapshotsFromDocuments(items)
3494
+ .pipe(rxjs$1.first())
3495
+ .subscribe((snapshots) => {
3496
+ expect(snapshots.length).toBe(testDocumentCount);
3497
+ snapshots.forEach((snapshot, i) => {
3498
+ expect(snapshot.data()).toBeDefined();
3499
+ expect(snapshot.id).toBe(items[i].id);
3500
+ });
3501
+ done();
3502
+ });
3503
+ }));
3504
+ it('should emit an empty array for empty input', test.callbackTest((done) => {
3505
+ sub.subscription = firebase.latestSnapshotsFromDocuments([])
3506
+ .pipe(rxjs$1.first())
3507
+ .subscribe((snapshots) => {
3508
+ expect(snapshots.length).toBe(0);
3509
+ done();
3510
+ });
3511
+ }));
3512
+ });
3513
+ // MARK: mapLatestSnapshotsFromDocuments
3514
+ describe('mapLatestSnapshotsFromDocuments()', () => {
3515
+ it('should apply the operator to each document stream', test.callbackTest((done) => {
3516
+ sub.subscription = firebase.mapLatestSnapshotsFromDocuments(items, rxjs$1.map((snapshot) => snapshot.id))
3517
+ .pipe(rxjs$1.first())
3518
+ .subscribe((ids) => {
3519
+ expect(ids.length).toBe(testDocumentCount);
3520
+ ids.forEach((id, i) => {
3521
+ expect(id).toBe(items[i].id);
3522
+ });
3523
+ done();
3524
+ });
3525
+ }));
3526
+ it('should emit an empty array for empty input', test.callbackTest((done) => {
3527
+ sub.subscription = firebase.mapLatestSnapshotsFromDocuments([], rxjs$1.map((snapshot) => snapshot.id))
3528
+ .pipe(rxjs$1.first())
3529
+ .subscribe((ids) => {
3530
+ expect(ids.length).toBe(0);
3531
+ done();
3532
+ });
3533
+ }));
3534
+ });
3535
+ // MARK: latestDataFromDocuments
3536
+ describe('latestDataFromDocuments()', () => {
3537
+ it('should emit data with id/key for all documents', test.callbackTest((done) => {
3538
+ sub.subscription = firebase.latestDataFromDocuments(items)
3539
+ .pipe(rxjs$1.first())
3540
+ .subscribe((data) => {
3541
+ expect(data.length).toBe(testDocumentCount);
3542
+ data.forEach((d) => {
3543
+ expect(d.id).toBeDefined();
3544
+ expect(d.key).toBeDefined();
3545
+ expect(d.test).toBe(true);
3546
+ });
3547
+ done();
3548
+ });
3549
+ }));
3550
+ it('should emit an empty array for empty input', test.callbackTest((done) => {
3551
+ sub.subscription = firebase.latestDataFromDocuments([])
3552
+ .pipe(rxjs$1.first())
3553
+ .subscribe((data) => {
3554
+ expect(data.length).toBe(0);
3555
+ done();
3556
+ });
3557
+ }));
3558
+ });
3559
+ // MARK: dataFromDocumentSnapshots operator
3560
+ describe('dataFromDocumentSnapshots()', () => {
3561
+ it('should transform snapshot arrays into data arrays with id/key', test.callbackTest((done) => {
3562
+ sub.subscription = firebase.latestSnapshotsFromDocuments(items)
3563
+ .pipe(firebase.dataFromDocumentSnapshots(), rxjs$1.first())
3564
+ .subscribe((data) => {
3565
+ expect(data.length).toBe(testDocumentCount);
3566
+ data.forEach((d) => {
3567
+ expect(d.id).toBeDefined();
3568
+ expect(d.key).toBeDefined();
3569
+ });
3570
+ done();
3571
+ });
3572
+ }));
3573
+ });
3574
+ // MARK: streamDocumentSnapshotDataPairs
3575
+ describe('streamDocumentSnapshotDataPairs()', () => {
3576
+ it('should emit snapshot-data pairs for all documents', test.callbackTest((done) => {
3577
+ sub.subscription = firebase.streamDocumentSnapshotDataPairs(items)
3578
+ .pipe(rxjs$1.first())
3579
+ .subscribe((pairs) => {
3580
+ expect(pairs.length).toBe(testDocumentCount);
3581
+ pairs.forEach((pair, i) => {
3582
+ expect(pair.document).toBe(items[i]);
3583
+ expect(pair.snapshot).toBeDefined();
3584
+ expect(pair.snapshot.data()).toBeDefined();
3585
+ expect(pair.data).toBeDefined();
3586
+ expect(pair.data.id).toBe(items[i].id);
3587
+ expect(pair.data.key).toBe(items[i].key);
3588
+ expect(pair.data.test).toBe(true);
3589
+ });
3590
+ done();
3591
+ });
3592
+ }));
3593
+ it('should emit an empty array for empty input', test.callbackTest((done) => {
3594
+ sub.subscription = firebase.streamDocumentSnapshotDataPairs([])
3595
+ .pipe(rxjs$1.first())
3596
+ .subscribe((pairs) => {
3597
+ expect(pairs.length).toBe(0);
3598
+ done();
3599
+ });
3600
+ }));
3601
+ });
3602
+ // MARK: streamDocumentSnapshotDataPairsWithData
3603
+ describe('streamDocumentSnapshotDataPairsWithData()', () => {
3604
+ it('should emit snapshot-data pairs for all existing documents', test.callbackTest((done) => {
3605
+ sub.subscription = firebase.streamDocumentSnapshotDataPairsWithData(items)
3606
+ .pipe(rxjs$1.first())
3607
+ .subscribe((pairs) => {
3608
+ expect(pairs.length).toBe(testDocumentCount);
3609
+ pairs.forEach((pair) => {
3610
+ expect(pair.data).toBeDefined();
3611
+ expect(pair.data.id).toBeDefined();
3612
+ expect(pair.data.key).toBeDefined();
3613
+ });
3614
+ done();
3615
+ });
3616
+ }));
3617
+ it('should filter out non-existent documents', test.callbackTest((done) => {
3618
+ const newDoc = firebase.newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3619
+ const allDocs = [...items, newDoc];
3620
+ sub.subscription = firebase.streamDocumentSnapshotDataPairsWithData(allDocs)
3621
+ .pipe(rxjs$1.first())
3622
+ .subscribe((pairs) => {
3623
+ expect(pairs.length).toBe(testDocumentCount);
3624
+ expect(pairs.every((p) => p.data != null)).toBe(true);
3625
+ done();
3626
+ });
3627
+ }));
3628
+ it('should emit an empty array for empty input', test.callbackTest((done) => {
3629
+ sub.subscription = firebase.streamDocumentSnapshotDataPairsWithData([])
3630
+ .pipe(rxjs$1.first())
3631
+ .subscribe((pairs) => {
3632
+ expect(pairs.length).toBe(0);
3633
+ done();
3634
+ });
3635
+ }));
3636
+ });
3637
+ });
3638
+ });
3639
+ }
3640
+
2914
3641
  /**
2915
3642
  * Describes accessor driver tests, using a MockItemCollectionFixture.
2916
3643
  *
@@ -2923,8 +3650,8 @@ function describeFirestoreIterationTests(f) {
2923
3650
  let items;
2924
3651
  let sub;
2925
3652
  beforeEach(async () => {
2926
- firestoreIteration = f.instance.firestoreCollection.firestoreIteration;
2927
- items = await firebase.makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
3653
+ firestoreIteration = f.instance.mockItemCollection.firestoreIteration;
3654
+ items = await firebase.makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2928
3655
  count: testDocumentCount,
2929
3656
  init: (i) => {
2930
3657
  return {
@@ -3832,6 +4559,7 @@ exports.clearTestFirestoreContextCollections = clearTestFirestoreContextCollecti
3832
4559
  exports.describeFirebaseStorageAccessorDriverTests = describeFirebaseStorageAccessorDriverTests;
3833
4560
  exports.describeFirestoreAccessorDriverTests = describeFirestoreAccessorDriverTests;
3834
4561
  exports.describeFirestoreDocumentAccessorTests = describeFirestoreDocumentAccessorTests;
4562
+ exports.describeFirestoreDocumentUtilityTests = describeFirestoreDocumentUtilityTests;
3835
4563
  exports.describeFirestoreIterationTests = describeFirestoreIterationTests;
3836
4564
  exports.describeFirestoreQueryDriverTests = describeFirestoreQueryDriverTests;
3837
4565
  exports.firebaseRulesUnitTestBuilder = firebaseRulesUnitTestBuilder;