@dereekb/firebase 13.0.5 → 13.0.7

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.esm.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { performAsyncTasks, cachedGetter, bitwiseObjectDencoder, modelFieldConversions, isEvenNumber, arrayFactory, mapGetter, randomNumberFactory, randomFromArrayFactory, idBatchFactory, unique, waitForMs, arrayContainsDuplicateValue, useCallback, readableStreamToBuffer, SLASH_PATH_SEPARATOR } from '@dereekb/util';
2
2
  import { AbstractTestContextFixture, testContextBuilder, instanceWrapTestContextFactory, AbstractWrappedFixtureWithInstance, itShouldFail, expectFail, callbackTest } from '@dereekb/util/test';
3
3
  import { initializeTestEnvironment } from '@firebase/rules-unit-testing';
4
- import { firebaseStorageClientDrivers, firebaseFirestoreClientDrivers, firestoreContextFactory, firebaseStorageContextFactory, firestoreModelIdentity, snapshotConverterFunctions, firestoreBoolean, optionalFirestoreNumber, optionalFirestoreDate, optionalFirestoreArray, optionalFirestoreString, firestoreDate, firestoreBitwiseObjectMap, firestoreUniqueStringArray, firestoreNumber, firestoreString, firestoreUID, copyUserRelatedDataAccessorFactoryFunction, firestoreSubObject, AbstractFirestoreDocumentWithParent, AbstractFirestoreDocument, firebaseModelServiceFactory, grantFullAccessIfAdmin, firebaseModelsService, systemStateFirestoreCollection, allChildDocumentsUnderParent, where, makeDocuments, getDocumentSnapshotPairs, useDocumentSnapshot, useDocumentSnapshotData, firestoreIdBatchVerifierFactory, whereDocumentId, loadAllFirestoreDocumentSnapshotPairs, loadAllFirestoreDocumentSnapshot, iterateFirestoreDocumentSnapshotPairs, iterateFirestoreDocumentSnapshots, iterateFirestoreDocumentSnapshotPairBatches, iterateFirestoreDocumentSnapshotBatches, limit, orderBy, limitToLast, whereStringHasRootIdentityModelKey, whereStringValueHasPrefix, whereDateIsAfterWithSort, whereDateIsBeforeWithSort, whereDateIsOnOrAfterWithSort, whereDateIsOnOrBeforeWithSort, whereDateIsInRange, whereDateIsBetween, startAt, orderByDocumentId, startAtValue, startAfter, endAt, endAtValue, endBefore, firebaseQuerySnapshotAccumulator, firebaseQueryItemAccumulator, uploadFileWithStream, iterateStorageListFilesByEachFile } from '@dereekb/firebase';
4
+ import { firebaseStorageClientDrivers, firebaseFirestoreClientDrivers, firestoreContextFactory, firebaseStorageContextFactory, firestoreModelIdentity, snapshotConverterFunctions, firestoreBoolean, optionalFirestoreNumber, optionalFirestoreDate, optionalFirestoreArray, optionalFirestoreString, firestoreDate, firestoreBitwiseObjectMap, firestoreUniqueStringArray, firestoreNumber, firestoreString, firestoreUID, copyUserRelatedDataAccessorFactoryFunction, firestoreSubObject, AbstractFirestoreDocumentWithParent, AbstractFirestoreDocument, firebaseModelServiceFactory, grantFullAccessIfAdmin, firebaseModelsService, systemStateFirestoreCollection, allChildDocumentsUnderParent, where, makeDocuments, getDocumentSnapshotPairs, useDocumentSnapshot, useDocumentSnapshotData, firestoreIdBatchVerifierFactory, whereDocumentId, loadAllFirestoreDocumentSnapshotPairs, loadAllFirestoreDocumentSnapshot, iterateFirestoreDocumentSnapshotPairs, iterateFirestoreDocumentSnapshots, iterateFirestoreDocumentSnapshotPairBatches, iterateFirestoreDocumentSnapshotBatches, limit, orderBy, limitToLast, whereStringHasRootIdentityModelKey, whereStringValueHasPrefix, whereDateIsAfterWithSort, whereDateIsBeforeWithSort, whereDateIsOnOrAfterWithSort, whereDateIsOnOrBeforeWithSort, whereDateIsInRange, whereDateIsBetween, startAt, orderByDocumentId, startAtValue, startAfter, endAt, endAtValue, endBefore, streamFromOnSnapshot, latestSnapshotsFromDocuments, newDocuments, getDocumentSnapshots, getDocumentSnapshotPair, getDocumentSnapshotDataPair, getDocumentSnapshotDataPairs, getDocumentSnapshotDataPairsWithData, getDocumentSnapshotDataTuples, getDocumentSnapshotData, getDocumentSnapshotsData, getDataFromDocumentSnapshots, loadDocumentsForSnapshots, loadDocumentsForDocumentReferences, loadDocumentsForDocumentReferencesFromValues, loadDocumentsForKeys, loadDocumentsForKeysFromValues, loadDocumentsForIds, loadDocumentsForIdsFromValues, firestoreDocumentLoader, firestoreDocumentSnapshotPairsLoader, documentData, documentDataFunction, documentDataWithIdAndKey, setIdAndKeyFromSnapshotOnDocumentData, setIdAndKeyFromKeyIdRefOnDocumentData, firestoreModelIdFromDocument, firestoreModelIdsFromDocuments, firestoreModelKeyFromDocument, firestoreModelKeysFromDocuments, documentReferenceFromDocument, documentReferencesFromDocuments, limitedFirestoreDocumentAccessorSnapshotCache, mapLatestSnapshotsFromDocuments, streamDocumentSnapshotsData, dataFromDocumentSnapshots, streamDocumentSnapshotDataPairs, streamDocumentSnapshotDataPairsWithData, firebaseQuerySnapshotAccumulator, firebaseQueryItemAccumulator, uploadFileWithStream, iterateStorageListFilesByEachFile } from '@dereekb/firebase';
5
5
  import { setLogLevel } from 'firebase/firestore';
6
- import { firstValueFrom, filter, skip, from, first, switchMap } from 'rxjs';
6
+ import { firstValueFrom, filter, skip, from, first, map, switchMap } from 'rxjs';
7
7
  import { SubscriptionObject, iteratorNextPageUntilPage, flattenAccumulatorResultItemArray, accumulatorCurrentPageListLoadingState, isLoadingStateFinishedLoading, accumulatorFlattenPageListLoadingState } from '@dereekb/rxjs';
8
8
  import { addDays, startOfDay, addHours } from 'date-fns';
9
9
  import { DateRangeType } from '@dereekb/date';
@@ -817,8 +817,8 @@ function describeFirestoreAccessorDriverTests(f) {
817
817
  let mockItemFirestoreDocumentAccessor;
818
818
  let items;
819
819
  beforeEach(async () => {
820
- mockItemFirestoreDocumentAccessor = f.instance.firestoreCollection.documentAccessor();
821
- items = await makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
820
+ mockItemFirestoreDocumentAccessor = f.instance.mockItemCollection.documentAccessor();
821
+ items = await makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
822
822
  count: testDocumentCount,
823
823
  init: (i) => {
824
824
  return {
@@ -843,8 +843,8 @@ function describeFirestoreAccessorDriverTests(f) {
843
843
  dataForUpdate: () => ({ test: false }),
844
844
  hasRemainingDataFromFirstOfTwoUpdate: (data) => (data.tags?.length || 0) > 0 && data.tags?.[0] === 'a',
845
845
  hasDataFromUpdate: (data) => data.test === false,
846
- loadDocumentForTransaction: (transaction, ref) => f.instance.firestoreCollection.documentAccessorForTransaction(transaction).loadDocument(ref),
847
- loadDocumentForWriteBatch: (writeBatch, ref) => f.instance.firestoreCollection.documentAccessorForWriteBatch(writeBatch).loadDocument(ref)
846
+ loadDocumentForTransaction: (transaction, ref) => f.instance.mockItemCollection.documentAccessorForTransaction(transaction).loadDocument(ref),
847
+ loadDocumentForWriteBatch: (writeBatch, ref) => f.instance.mockItemCollection.documentAccessorForWriteBatch(writeBatch).loadDocument(ref)
848
848
  }));
849
849
  describe('increment()', () => {
850
850
  it(`should increase the item's value`, async () => {
@@ -885,7 +885,7 @@ function describeFirestoreAccessorDriverTests(f) {
885
885
  it(`should increase the item's value`, async () => {
886
886
  const update = { number: 3 };
887
887
  await f.parent.firestoreContext.runTransaction(async (transaction) => {
888
- const itemDocumentInTransaction = await f.instance.firestoreCollection.documentAccessorForTransaction(transaction).loadDocumentForId(itemDocument.id);
888
+ const itemDocumentInTransaction = await f.instance.mockItemCollection.documentAccessorForTransaction(transaction).loadDocumentForId(itemDocument.id);
889
889
  const data = await itemDocumentInTransaction.snapshotData();
890
890
  expect(data?.number).toBe(undefined);
891
891
  await itemDocumentInTransaction.increment(update);
@@ -898,7 +898,7 @@ function describeFirestoreAccessorDriverTests(f) {
898
898
  it(`should increase the item's value`, async () => {
899
899
  const update = { number: 3 };
900
900
  const writeBatch = f.parent.firestoreContext.batch();
901
- const itemDocumentForWriteBatch = await f.instance.firestoreCollection.documentAccessorForWriteBatch(writeBatch).loadDocumentForId(itemDocument.id);
901
+ const itemDocumentForWriteBatch = await f.instance.mockItemCollection.documentAccessorForWriteBatch(writeBatch).loadDocumentForId(itemDocument.id);
902
902
  await itemDocumentForWriteBatch.increment(update);
903
903
  await writeBatch.commit();
904
904
  const result = await itemDocument.snapshotData();
@@ -1575,7 +1575,7 @@ function describeFirestoreQueryDriverTests(f) {
1575
1575
  const EVEN_TAG = 'even';
1576
1576
  const ODD_TAG = 'odd';
1577
1577
  beforeEach(async () => {
1578
- items = await makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
1578
+ items = await makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
1579
1579
  count: testDocumentCount,
1580
1580
  init: (i) => {
1581
1581
  return {
@@ -2164,7 +2164,7 @@ function describeFirestoreQueryDriverTests(f) {
2164
2164
  describe('queryDocument', () => {
2165
2165
  let queryDocument;
2166
2166
  beforeEach(async () => {
2167
- queryDocument = f.instance.firestoreCollection.queryDocument;
2167
+ queryDocument = f.instance.mockItemCollection.queryDocument;
2168
2168
  });
2169
2169
  describe('filter()', () => {
2170
2170
  it('should apply the filter to the query', async () => {
@@ -2268,7 +2268,7 @@ function describeFirestoreQueryDriverTests(f) {
2268
2268
  tryComplete();
2269
2269
  });
2270
2270
  // add one item
2271
- waitForMs(10).then(() => makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
2271
+ waitForMs(10).then(() => makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2272
2272
  count: itemsToAdd,
2273
2273
  init: (i) => {
2274
2274
  return {
@@ -2344,7 +2344,7 @@ function describeFirestoreQueryDriverTests(f) {
2344
2344
  tryComplete();
2345
2345
  });
2346
2346
  // add one item
2347
- waitForMs(10).then(() => makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
2347
+ waitForMs(10).then(() => makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2348
2348
  count: itemsToAdd,
2349
2349
  init: (i) => {
2350
2350
  return {
@@ -2388,7 +2388,7 @@ function describeFirestoreQueryDriverTests(f) {
2388
2388
  describe('query', () => {
2389
2389
  let query;
2390
2390
  beforeEach(async () => {
2391
- query = f.instance.firestoreCollection.query;
2391
+ query = f.instance.mockItemCollection.query;
2392
2392
  });
2393
2393
  describe('streamDocs()', () => {
2394
2394
  let sub;
@@ -2416,7 +2416,7 @@ function describeFirestoreQueryDriverTests(f) {
2416
2416
  tryComplete();
2417
2417
  });
2418
2418
  // add one item
2419
- waitForMs(10).then(() => makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
2419
+ waitForMs(10).then(() => makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2420
2420
  count: itemsToAdd,
2421
2421
  init: (i) => {
2422
2422
  return {
@@ -2641,7 +2641,7 @@ function describeFirestoreQueryDriverTests(f) {
2641
2641
  const oddPrefix = mockItemIdentity.collectionType + 'd' + '/'; // similar, but not quite the same
2642
2642
  const expectedNumberOfEvenValues = Math.ceil(testDocumentCount / 2);
2643
2643
  beforeEach(async () => {
2644
- items = await makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
2644
+ items = await makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2645
2645
  count: testDocumentCount,
2646
2646
  init: (i) => {
2647
2647
  const isEven = isEvenNumber(i);
@@ -2909,6 +2909,814 @@ function describeFirestoreQueryDriverTests(f) {
2909
2909
  });
2910
2910
  }
2911
2911
 
2912
+ /**
2913
+ * Describes utility driver tests, using a MockItemCollectionFixture.
2914
+ *
2915
+ * @param f
2916
+ */
2917
+ function describeFirestoreDocumentUtilityTests(f) {
2918
+ describe('FirestoreDocumentUtilities', () => {
2919
+ const testDocumentCount = 5;
2920
+ let items;
2921
+ const startDate = addDays(startOfDay(new Date()), 1);
2922
+ const EVEN_TAG = 'even';
2923
+ const ODD_TAG = 'odd';
2924
+ beforeEach(async () => {
2925
+ items = await makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2926
+ count: testDocumentCount,
2927
+ init: (i) => {
2928
+ return {
2929
+ value: `${i}`,
2930
+ number: i,
2931
+ date: addHours(startDate, i),
2932
+ tags: [`${i}`, `${isEvenNumber(i) ? EVEN_TAG : ODD_TAG}`],
2933
+ test: true
2934
+ };
2935
+ }
2936
+ });
2937
+ });
2938
+ let sub;
2939
+ beforeEach(() => {
2940
+ sub = new SubscriptionObject();
2941
+ });
2942
+ afterEach(() => {
2943
+ sub.destroy();
2944
+ });
2945
+ describe('query.util.ts', () => {
2946
+ // MARK: streamFromOnSnapshot
2947
+ describe('streamFromOnSnapshot()', () => {
2948
+ it('should call unsubscribe when the subscriber unsubscribes', () => {
2949
+ let unsubscribeCalled = false;
2950
+ const obs = streamFromOnSnapshot(({ next }) => {
2951
+ next('value');
2952
+ return () => {
2953
+ unsubscribeCalled = true;
2954
+ };
2955
+ });
2956
+ const subscription = obs.subscribe();
2957
+ expect(unsubscribeCalled).toBe(false);
2958
+ subscription.unsubscribe();
2959
+ expect(unsubscribeCalled).toBe(true);
2960
+ });
2961
+ it('should unsubscribe the onSnapshot listener when first() completes', () => {
2962
+ let unsubscribeCalled = false;
2963
+ const obs = streamFromOnSnapshot(({ next }) => {
2964
+ next('value');
2965
+ return () => {
2966
+ unsubscribeCalled = true;
2967
+ };
2968
+ });
2969
+ // first() completes the subscriber, then RxJS tears down the source
2970
+ const subscription = obs.pipe(first()).subscribe();
2971
+ // After first() + subscribe complete synchronously, teardown should have run
2972
+ expect(subscription.closed).toBe(true);
2973
+ expect(unsubscribeCalled).toBe(true);
2974
+ });
2975
+ it('should unsubscribe onSnapshot listeners from document streams after subscriber unsubscribes', callbackTest((done) => {
2976
+ sub.subscription = latestSnapshotsFromDocuments(items)
2977
+ .pipe(first())
2978
+ .subscribe({
2979
+ complete: () => {
2980
+ // With refCount: true on shareReplay, unsubscribing the last subscriber
2981
+ // tears down the source, which calls unsubscribe on each onSnapshot listener.
2982
+ // If this test completes without the "active listeners" error, the cleanup worked.
2983
+ done();
2984
+ }
2985
+ });
2986
+ }));
2987
+ });
2988
+ });
2989
+ describe('document.utility.ts', () => {
2990
+ // MARK: newDocuments
2991
+ describe('newDocuments()', () => {
2992
+ it('should create the specified number of document instances', () => {
2993
+ const count = 3;
2994
+ const docs = newDocuments(f.instance.mockItemCollection.documentAccessor(), count);
2995
+ expect(docs.length).toBe(count);
2996
+ });
2997
+ it('should create documents with unique IDs', () => {
2998
+ const docs = newDocuments(f.instance.mockItemCollection.documentAccessor(), 3);
2999
+ const ids = docs.map((x) => x.id);
3000
+ expect(unique(ids).length).toBe(3);
3001
+ });
3002
+ it('should not persist documents to Firestore', async () => {
3003
+ const docs = newDocuments(f.instance.mockItemCollection.documentAccessor(), 2);
3004
+ for (const doc of docs) {
3005
+ const exists = await doc.exists();
3006
+ expect(exists).toBe(false);
3007
+ }
3008
+ });
3009
+ });
3010
+ // MARK: makeDocuments
3011
+ describe('makeDocuments()', () => {
3012
+ it('should create and persist documents in Firestore', async () => {
3013
+ const count = 3;
3014
+ const docs = await makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
3015
+ count,
3016
+ init: (i) => ({ value: `test_${i}`, test: true })
3017
+ });
3018
+ expect(docs.length).toBe(count);
3019
+ for (const doc of docs) {
3020
+ const exists = await doc.exists();
3021
+ expect(exists).toBe(true);
3022
+ }
3023
+ });
3024
+ it('should not persist documents when init returns null', async () => {
3025
+ const docs = await makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
3026
+ count: 2,
3027
+ init: () => null
3028
+ });
3029
+ expect(docs.length).toBe(2);
3030
+ for (const doc of docs) {
3031
+ const exists = await doc.exists();
3032
+ expect(exists).toBe(false);
3033
+ }
3034
+ });
3035
+ it('should use custom newDocument factory when provided', async () => {
3036
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3037
+ const customId = 'custom_doc_id';
3038
+ const docs = await makeDocuments(accessor, {
3039
+ count: 1,
3040
+ newDocument: (acc) => acc.loadDocumentForId(customId),
3041
+ init: () => ({ value: 'custom', test: true })
3042
+ });
3043
+ expect(docs.length).toBe(1);
3044
+ expect(docs[0].id).toBe(customId);
3045
+ const exists = await docs[0].exists();
3046
+ expect(exists).toBe(true);
3047
+ });
3048
+ });
3049
+ // MARK: getDocumentSnapshots
3050
+ describe('getDocumentSnapshots()', () => {
3051
+ it('should return snapshots for all documents', async () => {
3052
+ const snapshots = await getDocumentSnapshots(items);
3053
+ expect(snapshots.length).toBe(testDocumentCount);
3054
+ snapshots.forEach((snapshot) => {
3055
+ expect(snapshot.data()).toBeDefined();
3056
+ });
3057
+ });
3058
+ it('should preserve ordering of the input array', async () => {
3059
+ const snapshots = await getDocumentSnapshots(items);
3060
+ for (let i = 0; i < items.length; i++) {
3061
+ expect(snapshots[i].id).toBe(items[i].id);
3062
+ }
3063
+ });
3064
+ });
3065
+ // MARK: getDocumentSnapshotPair
3066
+ describe('getDocumentSnapshotPair()', () => {
3067
+ it('should return a pair with both document and snapshot', async () => {
3068
+ const pair = await getDocumentSnapshotPair(items[0]);
3069
+ expect(pair.document).toBe(items[0]);
3070
+ expect(pair.snapshot).toBeDefined();
3071
+ expect(pair.snapshot.data()).toBeDefined();
3072
+ expect(pair.snapshot.id).toBe(items[0].id);
3073
+ });
3074
+ });
3075
+ // MARK: getDocumentSnapshotPairs
3076
+ describe('getDocumentSnapshotPairs()', () => {
3077
+ it('should return pairs for all documents', async () => {
3078
+ const pairs = await getDocumentSnapshotPairs(items);
3079
+ expect(pairs.length).toBe(testDocumentCount);
3080
+ pairs.forEach((pair, i) => {
3081
+ expect(pair.document).toBe(items[i]);
3082
+ expect(pair.snapshot.data()).toBeDefined();
3083
+ });
3084
+ });
3085
+ });
3086
+ // MARK: getDocumentSnapshotDataPair
3087
+ describe('getDocumentSnapshotDataPair()', () => {
3088
+ it('should return a triplet with document, snapshot, and data with id/key', async () => {
3089
+ const pair = await getDocumentSnapshotDataPair(items[0]);
3090
+ expect(pair.document).toBe(items[0]);
3091
+ expect(pair.snapshot).toBeDefined();
3092
+ expect(pair.data).toBeDefined();
3093
+ expect(pair.data.id).toBe(items[0].id);
3094
+ expect(pair.data.key).toBe(items[0].key);
3095
+ expect(pair.data.test).toBe(true);
3096
+ });
3097
+ it('should return undefined data for a non-existent document', async () => {
3098
+ const newDoc = newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3099
+ const pair = await getDocumentSnapshotDataPair(newDoc);
3100
+ expect(pair.document).toBe(newDoc);
3101
+ expect(pair.data).toBeUndefined();
3102
+ });
3103
+ });
3104
+ // MARK: getDocumentSnapshotDataPairs
3105
+ describe('getDocumentSnapshotDataPairs()', () => {
3106
+ it('should return triplets for all documents', async () => {
3107
+ const pairs = await getDocumentSnapshotDataPairs(items);
3108
+ expect(pairs.length).toBe(testDocumentCount);
3109
+ pairs.forEach((pair, i) => {
3110
+ expect(pair.document).toBe(items[i]);
3111
+ expect(pair.data).toBeDefined();
3112
+ expect(pair.data.id).toBe(items[i].id);
3113
+ expect(pair.data.key).toBe(items[i].key);
3114
+ });
3115
+ });
3116
+ });
3117
+ // MARK: getDocumentSnapshotDataPairsWithData
3118
+ describe('getDocumentSnapshotDataPairsWithData()', () => {
3119
+ it('should return triplets for all existing documents', async () => {
3120
+ const pairs = await getDocumentSnapshotDataPairsWithData(items);
3121
+ expect(pairs.length).toBe(testDocumentCount);
3122
+ pairs.forEach((pair) => {
3123
+ expect(pair.data).toBeDefined();
3124
+ expect(pair.data.id).toBeDefined();
3125
+ expect(pair.data.key).toBeDefined();
3126
+ });
3127
+ });
3128
+ it('should filter out non-existent documents', async () => {
3129
+ const newDoc = newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3130
+ const allDocs = [...items, newDoc];
3131
+ const pairs = await getDocumentSnapshotDataPairsWithData(allDocs);
3132
+ expect(pairs.length).toBe(testDocumentCount);
3133
+ expect(pairs.every((p) => p.data != null)).toBe(true);
3134
+ });
3135
+ });
3136
+ // MARK: getDocumentSnapshotDataTuples
3137
+ describe('getDocumentSnapshotDataTuples()', () => {
3138
+ it('should return [document, data] tuples for all documents', async () => {
3139
+ const tuples = await getDocumentSnapshotDataTuples(items);
3140
+ expect(tuples.length).toBe(testDocumentCount);
3141
+ tuples.forEach((tuple, i) => {
3142
+ expect(tuple[0]).toBe(items[i]);
3143
+ expect(tuple[1]).toBeDefined();
3144
+ expect(tuple[1].test).toBe(true);
3145
+ });
3146
+ });
3147
+ it('should return undefined data for non-existent documents', async () => {
3148
+ const newDoc = newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3149
+ const tuples = await getDocumentSnapshotDataTuples([newDoc]);
3150
+ expect(tuples.length).toBe(1);
3151
+ expect(tuples[0][0]).toBe(newDoc);
3152
+ expect(tuples[0][1]).toBeUndefined();
3153
+ });
3154
+ });
3155
+ // MARK: getDocumentSnapshotData
3156
+ describe('getDocumentSnapshotData()', () => {
3157
+ it('should return data with id and key by default', async () => {
3158
+ const data = await getDocumentSnapshotData(items[0]);
3159
+ expect(data).toBeDefined();
3160
+ expect(data.id).toBe(items[0].id);
3161
+ expect(data.key).toBe(items[0].key);
3162
+ expect(data.test).toBe(true);
3163
+ });
3164
+ it('should return data with id and key when withId is true', async () => {
3165
+ const data = await getDocumentSnapshotData(items[0], true);
3166
+ expect(data).toBeDefined();
3167
+ expect(data.id).toBe(items[0].id);
3168
+ expect(data.key).toBe(items[0].key);
3169
+ });
3170
+ it('should return raw data without id/key when withId is false', async () => {
3171
+ const data = await getDocumentSnapshotData(items[0], false);
3172
+ expect(data).toBeDefined();
3173
+ expect(data.test).toBe(true);
3174
+ expect(data.id).toBeUndefined();
3175
+ expect(data.key).toBeUndefined();
3176
+ });
3177
+ it('should return undefined for a non-existent document', async () => {
3178
+ const newDoc = newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3179
+ const data = await getDocumentSnapshotData(newDoc);
3180
+ expect(data).toBeUndefined();
3181
+ });
3182
+ });
3183
+ // MARK: getDocumentSnapshotsData
3184
+ describe('getDocumentSnapshotsData()', () => {
3185
+ it('should return data for all existing documents with id/key by default', async () => {
3186
+ const data = await getDocumentSnapshotsData(items);
3187
+ expect(data.length).toBe(testDocumentCount);
3188
+ data.forEach((d) => {
3189
+ expect(d.id).toBeDefined();
3190
+ expect(d.key).toBeDefined();
3191
+ expect(d.test).toBe(true);
3192
+ });
3193
+ });
3194
+ it('should return raw data without id/key when withId is false', async () => {
3195
+ const data = await getDocumentSnapshotsData(items, false);
3196
+ expect(data.length).toBe(testDocumentCount);
3197
+ data.forEach((d) => {
3198
+ expect(d.test).toBe(true);
3199
+ });
3200
+ });
3201
+ });
3202
+ // MARK: getDataFromDocumentSnapshots
3203
+ describe('getDataFromDocumentSnapshots()', () => {
3204
+ it('should extract data with id/key from snapshots', async () => {
3205
+ const snapshots = await getDocumentSnapshots(items);
3206
+ const data = getDataFromDocumentSnapshots(snapshots);
3207
+ expect(data.length).toBe(testDocumentCount);
3208
+ data.forEach((d) => {
3209
+ expect(d.id).toBeDefined();
3210
+ expect(d.key).toBeDefined();
3211
+ });
3212
+ });
3213
+ it('should extract raw data when withId is false', async () => {
3214
+ const snapshots = await getDocumentSnapshots(items);
3215
+ const data = getDataFromDocumentSnapshots(snapshots, false);
3216
+ expect(data.length).toBe(testDocumentCount);
3217
+ });
3218
+ it('should filter out non-existent document snapshots', async () => {
3219
+ const newDoc = newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3220
+ const allDocs = [...items, newDoc];
3221
+ const snapshots = await getDocumentSnapshots(allDocs);
3222
+ const data = getDataFromDocumentSnapshots(snapshots);
3223
+ expect(data.length).toBe(testDocumentCount);
3224
+ });
3225
+ });
3226
+ // MARK: loadDocumentsForSnapshots
3227
+ describe('loadDocumentsForSnapshots()', () => {
3228
+ it('should load documents from a query snapshot', async () => {
3229
+ const querySnapshot = await f.instance.mockItemCollection.query(where('test', '==', true)).getDocs();
3230
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3231
+ const docs = loadDocumentsForSnapshots(accessor, querySnapshot);
3232
+ expect(docs.length).toBe(testDocumentCount);
3233
+ docs.forEach((doc) => {
3234
+ expect(doc).toBeDefined();
3235
+ expect(doc.id).toBeDefined();
3236
+ });
3237
+ });
3238
+ });
3239
+ // MARK: loadDocumentsForDocumentReferences
3240
+ describe('loadDocumentsForDocumentReferences()', () => {
3241
+ it('should load documents from references', () => {
3242
+ const refs = items.map((x) => x.documentRef);
3243
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3244
+ const docs = loadDocumentsForDocumentReferences(accessor, refs);
3245
+ expect(docs.length).toBe(testDocumentCount);
3246
+ docs.forEach((doc, i) => {
3247
+ expect(doc.id).toBe(items[i].id);
3248
+ });
3249
+ });
3250
+ });
3251
+ // MARK: loadDocumentsForDocumentReferencesFromValues
3252
+ describe('loadDocumentsForDocumentReferencesFromValues()', () => {
3253
+ it('should extract refs from values and load documents', () => {
3254
+ const values = items.map((x) => ({ ref: x.documentRef }));
3255
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3256
+ const docs = loadDocumentsForDocumentReferencesFromValues(accessor, values, (v) => v.ref);
3257
+ expect(docs.length).toBe(testDocumentCount);
3258
+ docs.forEach((doc, i) => {
3259
+ expect(doc.id).toBe(items[i].id);
3260
+ });
3261
+ });
3262
+ });
3263
+ // MARK: loadDocumentsForKeys
3264
+ describe('loadDocumentsForKeys()', () => {
3265
+ it('should load documents from keys', () => {
3266
+ const keys = items.map((x) => x.key);
3267
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3268
+ const docs = loadDocumentsForKeys(accessor, keys);
3269
+ expect(docs.length).toBe(testDocumentCount);
3270
+ docs.forEach((doc, i) => {
3271
+ expect(doc.key).toBe(items[i].key);
3272
+ });
3273
+ });
3274
+ });
3275
+ // MARK: loadDocumentsForKeysFromValues
3276
+ describe('loadDocumentsForKeysFromValues()', () => {
3277
+ it('should extract keys from values and load documents', () => {
3278
+ const values = items.map((x) => ({ k: x.key }));
3279
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3280
+ const docs = loadDocumentsForKeysFromValues(accessor, values, (v) => v.k);
3281
+ expect(docs.length).toBe(testDocumentCount);
3282
+ docs.forEach((doc, i) => {
3283
+ expect(doc.key).toBe(items[i].key);
3284
+ });
3285
+ });
3286
+ });
3287
+ // MARK: loadDocumentsForIds
3288
+ describe('loadDocumentsForIds()', () => {
3289
+ it('should load documents from IDs', () => {
3290
+ const ids = items.map((x) => x.id);
3291
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3292
+ const docs = loadDocumentsForIds(accessor, ids);
3293
+ expect(docs.length).toBe(testDocumentCount);
3294
+ docs.forEach((doc, i) => {
3295
+ expect(doc.id).toBe(items[i].id);
3296
+ });
3297
+ });
3298
+ });
3299
+ // MARK: loadDocumentsForIdsFromValues
3300
+ describe('loadDocumentsForIdsFromValues()', () => {
3301
+ it('should extract IDs from values and load documents', () => {
3302
+ const values = items.map((x) => ({ docId: x.id }));
3303
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3304
+ const docs = loadDocumentsForIdsFromValues(accessor, values, (v) => v.docId);
3305
+ expect(docs.length).toBe(testDocumentCount);
3306
+ docs.forEach((doc, i) => {
3307
+ expect(doc.id).toBe(items[i].id);
3308
+ });
3309
+ });
3310
+ });
3311
+ // MARK: firestoreDocumentLoader
3312
+ describe('firestoreDocumentLoader()', () => {
3313
+ it('should create a loader that loads documents from references', () => {
3314
+ const loader = firestoreDocumentLoader(f.instance.mockItemCollection);
3315
+ const refs = items.map((x) => x.documentRef);
3316
+ const docs = loader(refs);
3317
+ expect(docs.length).toBe(testDocumentCount);
3318
+ docs.forEach((doc, i) => {
3319
+ expect(doc.id).toBe(items[i].id);
3320
+ });
3321
+ });
3322
+ });
3323
+ // MARK: firestoreDocumentSnapshotPairsLoader
3324
+ describe('firestoreDocumentSnapshotPairsLoader()', () => {
3325
+ it('should create a loader that converts snapshots to data pairs', async () => {
3326
+ const loader = firestoreDocumentSnapshotPairsLoader(f.instance.mockItemCollection);
3327
+ const snapshots = await getDocumentSnapshots(items);
3328
+ const pairs = loader(snapshots);
3329
+ expect(pairs.length).toBe(testDocumentCount);
3330
+ pairs.forEach((pair) => {
3331
+ expect(pair.document).toBeDefined();
3332
+ expect(pair.snapshot).toBeDefined();
3333
+ expect(pair.data).toBeDefined();
3334
+ expect(pair.data.id).toBeDefined();
3335
+ expect(pair.data.key).toBeDefined();
3336
+ });
3337
+ });
3338
+ });
3339
+ // MARK: documentData
3340
+ describe('documentData()', () => {
3341
+ it('should return data with id/key when withId is true', async () => {
3342
+ const snapshot = await items[0].accessor.get();
3343
+ const data = documentData(snapshot, true);
3344
+ expect(data).toBeDefined();
3345
+ expect(data.id).toBe(items[0].id);
3346
+ expect(data.key).toBe(items[0].key);
3347
+ });
3348
+ it('should return raw data when withId is false', async () => {
3349
+ const snapshot = await items[0].accessor.get();
3350
+ const data = documentData(snapshot, false);
3351
+ expect(data).toBeDefined();
3352
+ expect(data.test).toBe(true);
3353
+ });
3354
+ });
3355
+ // MARK: documentDataFunction
3356
+ describe('documentDataFunction()', () => {
3357
+ it('should return a function that extracts data with id/key when withId is true', async () => {
3358
+ const fn = documentDataFunction(true);
3359
+ const snapshot = await items[0].accessor.get();
3360
+ const data = fn(snapshot);
3361
+ expect(data).toBeDefined();
3362
+ expect(data.id).toBe(items[0].id);
3363
+ expect(data.key).toBe(items[0].key);
3364
+ });
3365
+ it('should return a function that extracts raw data when withId is false', async () => {
3366
+ const fn = documentDataFunction(false);
3367
+ const snapshot = await items[0].accessor.get();
3368
+ const data = fn(snapshot);
3369
+ expect(data).toBeDefined();
3370
+ expect(data.id).toBeUndefined();
3371
+ });
3372
+ });
3373
+ // MARK: documentDataWithIdAndKey
3374
+ describe('documentDataWithIdAndKey()', () => {
3375
+ it('should extract data with id and key from a snapshot', async () => {
3376
+ const snapshot = await items[0].accessor.get();
3377
+ const data = documentDataWithIdAndKey(snapshot);
3378
+ expect(data).toBeDefined();
3379
+ expect(data.id).toBe(items[0].id);
3380
+ expect(data.key).toBe(items[0].key);
3381
+ expect(data.test).toBe(true);
3382
+ });
3383
+ it('should return undefined for a non-existent document', async () => {
3384
+ const newDoc = newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3385
+ const snapshot = await newDoc.accessor.get();
3386
+ const data = documentDataWithIdAndKey(snapshot);
3387
+ expect(data).toBeUndefined();
3388
+ });
3389
+ });
3390
+ // MARK: setIdAndKeyFromSnapshotOnDocumentData
3391
+ describe('setIdAndKeyFromSnapshotOnDocumentData()', () => {
3392
+ it('should mutate data in-place to add id and key from snapshot', async () => {
3393
+ const snapshot = await items[0].accessor.get();
3394
+ const rawData = { test: true };
3395
+ const result = setIdAndKeyFromSnapshotOnDocumentData(rawData, snapshot);
3396
+ expect(result.id).toBe(items[0].id);
3397
+ expect(result.key).toBe(items[0].key);
3398
+ expect(result).toBe(rawData); // same reference
3399
+ });
3400
+ });
3401
+ // MARK: setIdAndKeyFromKeyIdRefOnDocumentData
3402
+ describe('setIdAndKeyFromKeyIdRefOnDocumentData()', () => {
3403
+ it('should mutate data in-place to add id and key from model ref', () => {
3404
+ const rawData = { test: true };
3405
+ const modelRef = { id: items[0].id, key: items[0].key };
3406
+ const result = setIdAndKeyFromKeyIdRefOnDocumentData(rawData, modelRef);
3407
+ expect(result.id).toBe(items[0].id);
3408
+ expect(result.key).toBe(items[0].key);
3409
+ expect(result).toBe(rawData);
3410
+ });
3411
+ });
3412
+ // MARK: useDocumentSnapshot
3413
+ describe('useDocumentSnapshot()', () => {
3414
+ it('should fetch snapshot and pass it to the use callback', async () => {
3415
+ const result = await useDocumentSnapshot(items[0], (snapshot) => {
3416
+ expect(snapshot.data()).toBeDefined();
3417
+ return snapshot.id;
3418
+ });
3419
+ expect(result).toBe(items[0].id);
3420
+ });
3421
+ it('should return default value when document is null', async () => {
3422
+ const result = await useDocumentSnapshot(null, () => 'used', 'default');
3423
+ expect(result).toBe('default');
3424
+ });
3425
+ });
3426
+ // MARK: useDocumentSnapshotData
3427
+ describe('useDocumentSnapshotData()', () => {
3428
+ it('should fetch snapshot data and pass it to the use callback', async () => {
3429
+ const result = await useDocumentSnapshotData(items[0], (data) => {
3430
+ expect(data.test).toBe(true);
3431
+ return data.value;
3432
+ });
3433
+ expect(result).toBe('0');
3434
+ });
3435
+ it('should return default value when document is null', async () => {
3436
+ const result = await useDocumentSnapshotData(null, () => 'used', 'default');
3437
+ expect(result).toBe('default');
3438
+ });
3439
+ });
3440
+ // MARK: Key Accessors
3441
+ describe('firestoreModelIdFromDocument()', () => {
3442
+ it('should return the document ID', () => {
3443
+ const id = firestoreModelIdFromDocument(items[0]);
3444
+ expect(id).toBe(items[0].id);
3445
+ });
3446
+ });
3447
+ describe('firestoreModelIdsFromDocuments()', () => {
3448
+ it('should return IDs for all documents', () => {
3449
+ const ids = firestoreModelIdsFromDocuments(items);
3450
+ expect(ids.length).toBe(testDocumentCount);
3451
+ ids.forEach((id, i) => {
3452
+ expect(id).toBe(items[i].id);
3453
+ });
3454
+ });
3455
+ });
3456
+ describe('firestoreModelKeyFromDocument()', () => {
3457
+ it('should return the full Firestore path', () => {
3458
+ const key = firestoreModelKeyFromDocument(items[0]);
3459
+ expect(key).toBe(items[0].key);
3460
+ });
3461
+ });
3462
+ describe('firestoreModelKeysFromDocuments()', () => {
3463
+ it('should return keys for all documents', () => {
3464
+ const keys = firestoreModelKeysFromDocuments(items);
3465
+ expect(keys.length).toBe(testDocumentCount);
3466
+ keys.forEach((key, i) => {
3467
+ expect(key).toBe(items[i].key);
3468
+ });
3469
+ });
3470
+ });
3471
+ describe('documentReferenceFromDocument()', () => {
3472
+ it('should return the document reference', () => {
3473
+ const ref = documentReferenceFromDocument(items[0]);
3474
+ expect(ref).toBe(items[0].documentRef);
3475
+ });
3476
+ });
3477
+ describe('documentReferencesFromDocuments()', () => {
3478
+ it('should return references for all documents', () => {
3479
+ const refs = documentReferencesFromDocuments(items);
3480
+ expect(refs.length).toBe(testDocumentCount);
3481
+ refs.forEach((ref, i) => {
3482
+ expect(ref).toBe(items[i].documentRef);
3483
+ });
3484
+ });
3485
+ });
3486
+ // MARK: limitedFirestoreDocumentAccessorSnapshotCache
3487
+ describe('limitedFirestoreDocumentAccessorSnapshotCache()', () => {
3488
+ it('should expose the underlying accessor', () => {
3489
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3490
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3491
+ expect(cache.accessor).toBe(accessor);
3492
+ });
3493
+ describe('getDocumentSnapshotDataPairForKey()', () => {
3494
+ it('should return a snapshot data pair for an existing document', async () => {
3495
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3496
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3497
+ const pair = await cache.getDocumentSnapshotDataPairForKey(items[0].key);
3498
+ expect(pair.document).toBeDefined();
3499
+ expect(pair.document.key).toBe(items[0].key);
3500
+ expect(pair.snapshot).toBeDefined();
3501
+ expect(pair.data).toBeDefined();
3502
+ expect(pair.data.id).toBe(items[0].id);
3503
+ expect(pair.data.key).toBe(items[0].key);
3504
+ });
3505
+ it('should return the same promise for the same key', async () => {
3506
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3507
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3508
+ const promise1 = cache.getDocumentSnapshotDataPairForKey(items[0].key);
3509
+ const promise2 = cache.getDocumentSnapshotDataPairForKey(items[0].key);
3510
+ expect(promise1).toBe(promise2);
3511
+ });
3512
+ it('should return undefined data for a non-existent document', async () => {
3513
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3514
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3515
+ const newDoc = newDocuments(accessor, 1)[0];
3516
+ const pair = await cache.getDocumentSnapshotDataPairForKey(newDoc.key);
3517
+ expect(pair.document).toBeDefined();
3518
+ expect(pair.data).toBeUndefined();
3519
+ });
3520
+ });
3521
+ describe('getDocumentSnapshotDataPairsForKeys()', () => {
3522
+ it('should return pairs for all keys in order', async () => {
3523
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3524
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3525
+ const keys = items.map((x) => x.key);
3526
+ const pairs = await cache.getDocumentSnapshotDataPairsForKeys(keys);
3527
+ expect(pairs.length).toBe(testDocumentCount);
3528
+ pairs.forEach((pair, i) => {
3529
+ expect(pair.document.key).toBe(items[i].key);
3530
+ expect(pair.data).toBeDefined();
3531
+ expect(pair.data.id).toBe(items[i].id);
3532
+ });
3533
+ });
3534
+ it('should use the cache for duplicate keys', async () => {
3535
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3536
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3537
+ const key = items[0].key;
3538
+ const [pair1] = await cache.getDocumentSnapshotDataPairsForKeys([key]);
3539
+ const [pair2] = await cache.getDocumentSnapshotDataPairsForKeys([key]);
3540
+ expect(pair1).toBe(pair2);
3541
+ });
3542
+ });
3543
+ describe('getDocumentSnapshotDataPairsWithDataForKeys()', () => {
3544
+ it('should return pairs for all existing documents', async () => {
3545
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3546
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3547
+ const keys = items.map((x) => x.key);
3548
+ const pairs = await cache.getDocumentSnapshotDataPairsWithDataForKeys(keys);
3549
+ expect(pairs.length).toBe(testDocumentCount);
3550
+ pairs.forEach((pair) => {
3551
+ expect(pair.data).toBeDefined();
3552
+ expect(pair.data.id).toBeDefined();
3553
+ expect(pair.data.key).toBeDefined();
3554
+ });
3555
+ });
3556
+ it('should filter out non-existent documents', async () => {
3557
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3558
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3559
+ const newDoc = newDocuments(accessor, 1)[0];
3560
+ const keys = [...items.map((x) => x.key), newDoc.key];
3561
+ const pairs = await cache.getDocumentSnapshotDataPairsWithDataForKeys(keys);
3562
+ expect(pairs.length).toBe(testDocumentCount);
3563
+ expect(pairs.every((p) => p.data != null)).toBe(true);
3564
+ });
3565
+ });
3566
+ });
3567
+ });
3568
+ describe('document.rxjs.ts', () => {
3569
+ // MARK: latestSnapshotsFromDocuments
3570
+ describe('latestSnapshotsFromDocuments()', () => {
3571
+ it('should emit snapshots for all documents', callbackTest((done) => {
3572
+ sub.subscription = latestSnapshotsFromDocuments(items)
3573
+ .pipe(first())
3574
+ .subscribe((snapshots) => {
3575
+ expect(snapshots.length).toBe(testDocumentCount);
3576
+ snapshots.forEach((snapshot, i) => {
3577
+ expect(snapshot.data()).toBeDefined();
3578
+ expect(snapshot.id).toBe(items[i].id);
3579
+ });
3580
+ done();
3581
+ });
3582
+ }));
3583
+ it('should emit an empty array for empty input', callbackTest((done) => {
3584
+ sub.subscription = latestSnapshotsFromDocuments([])
3585
+ .pipe(first())
3586
+ .subscribe((snapshots) => {
3587
+ expect(snapshots.length).toBe(0);
3588
+ done();
3589
+ });
3590
+ }));
3591
+ });
3592
+ // MARK: mapLatestSnapshotsFromDocuments
3593
+ describe('mapLatestSnapshotsFromDocuments()', () => {
3594
+ it('should apply the operator to each document stream', callbackTest((done) => {
3595
+ sub.subscription = mapLatestSnapshotsFromDocuments(items, map((snapshot) => snapshot.id))
3596
+ .pipe(first())
3597
+ .subscribe((ids) => {
3598
+ expect(ids.length).toBe(testDocumentCount);
3599
+ ids.forEach((id, i) => {
3600
+ expect(id).toBe(items[i].id);
3601
+ });
3602
+ done();
3603
+ });
3604
+ }));
3605
+ it('should emit an empty array for empty input', callbackTest((done) => {
3606
+ sub.subscription = mapLatestSnapshotsFromDocuments([], map((snapshot) => snapshot.id))
3607
+ .pipe(first())
3608
+ .subscribe((ids) => {
3609
+ expect(ids.length).toBe(0);
3610
+ done();
3611
+ });
3612
+ }));
3613
+ });
3614
+ // MARK: streamDocumentSnapshotsData
3615
+ describe('streamDocumentSnapshotsData()', () => {
3616
+ it('should emit data with id/key for all documents', callbackTest((done) => {
3617
+ sub.subscription = streamDocumentSnapshotsData(items)
3618
+ .pipe(first())
3619
+ .subscribe((data) => {
3620
+ expect(data.length).toBe(testDocumentCount);
3621
+ data.forEach((d) => {
3622
+ expect(d.id).toBeDefined();
3623
+ expect(d.key).toBeDefined();
3624
+ expect(d.test).toBe(true);
3625
+ });
3626
+ done();
3627
+ });
3628
+ }));
3629
+ it('should emit an empty array for empty input', callbackTest((done) => {
3630
+ sub.subscription = streamDocumentSnapshotsData([])
3631
+ .pipe(first())
3632
+ .subscribe((data) => {
3633
+ expect(data.length).toBe(0);
3634
+ done();
3635
+ });
3636
+ }));
3637
+ });
3638
+ // MARK: dataFromDocumentSnapshots operator
3639
+ describe('dataFromDocumentSnapshots()', () => {
3640
+ it('should transform snapshot arrays into data arrays with id/key', callbackTest((done) => {
3641
+ sub.subscription = latestSnapshotsFromDocuments(items)
3642
+ .pipe(dataFromDocumentSnapshots(), first())
3643
+ .subscribe((data) => {
3644
+ expect(data.length).toBe(testDocumentCount);
3645
+ data.forEach((d) => {
3646
+ expect(d.id).toBeDefined();
3647
+ expect(d.key).toBeDefined();
3648
+ });
3649
+ done();
3650
+ });
3651
+ }));
3652
+ });
3653
+ // MARK: streamDocumentSnapshotDataPairs
3654
+ describe('streamDocumentSnapshotDataPairs()', () => {
3655
+ it('should emit snapshot-data pairs for all documents', callbackTest((done) => {
3656
+ sub.subscription = streamDocumentSnapshotDataPairs(items)
3657
+ .pipe(first())
3658
+ .subscribe((pairs) => {
3659
+ expect(pairs.length).toBe(testDocumentCount);
3660
+ pairs.forEach((pair, i) => {
3661
+ expect(pair.document).toBe(items[i]);
3662
+ expect(pair.snapshot).toBeDefined();
3663
+ expect(pair.snapshot.data()).toBeDefined();
3664
+ expect(pair.data).toBeDefined();
3665
+ expect(pair.data.id).toBe(items[i].id);
3666
+ expect(pair.data.key).toBe(items[i].key);
3667
+ expect(pair.data.test).toBe(true);
3668
+ });
3669
+ done();
3670
+ });
3671
+ }));
3672
+ it('should emit an empty array for empty input', callbackTest((done) => {
3673
+ sub.subscription = streamDocumentSnapshotDataPairs([])
3674
+ .pipe(first())
3675
+ .subscribe((pairs) => {
3676
+ expect(pairs.length).toBe(0);
3677
+ done();
3678
+ });
3679
+ }));
3680
+ });
3681
+ // MARK: streamDocumentSnapshotDataPairsWithData
3682
+ describe('streamDocumentSnapshotDataPairsWithData()', () => {
3683
+ it('should emit snapshot-data pairs for all existing documents', callbackTest((done) => {
3684
+ sub.subscription = streamDocumentSnapshotDataPairsWithData(items)
3685
+ .pipe(first())
3686
+ .subscribe((pairs) => {
3687
+ expect(pairs.length).toBe(testDocumentCount);
3688
+ pairs.forEach((pair) => {
3689
+ expect(pair.data).toBeDefined();
3690
+ expect(pair.data.id).toBeDefined();
3691
+ expect(pair.data.key).toBeDefined();
3692
+ });
3693
+ done();
3694
+ });
3695
+ }));
3696
+ it('should filter out non-existent documents', callbackTest((done) => {
3697
+ const newDoc = newDocuments(f.instance.mockItemCollection.documentAccessor(), 1)[0];
3698
+ const allDocs = [...items, newDoc];
3699
+ sub.subscription = streamDocumentSnapshotDataPairsWithData(allDocs)
3700
+ .pipe(first())
3701
+ .subscribe((pairs) => {
3702
+ expect(pairs.length).toBe(testDocumentCount);
3703
+ expect(pairs.every((p) => p.data != null)).toBe(true);
3704
+ done();
3705
+ });
3706
+ }));
3707
+ it('should emit an empty array for empty input', callbackTest((done) => {
3708
+ sub.subscription = streamDocumentSnapshotDataPairsWithData([])
3709
+ .pipe(first())
3710
+ .subscribe((pairs) => {
3711
+ expect(pairs.length).toBe(0);
3712
+ done();
3713
+ });
3714
+ }));
3715
+ });
3716
+ });
3717
+ });
3718
+ }
3719
+
2912
3720
  /**
2913
3721
  * Describes accessor driver tests, using a MockItemCollectionFixture.
2914
3722
  *
@@ -2921,8 +3729,8 @@ function describeFirestoreIterationTests(f) {
2921
3729
  let items;
2922
3730
  let sub;
2923
3731
  beforeEach(async () => {
2924
- firestoreIteration = f.instance.firestoreCollection.firestoreIteration;
2925
- items = await makeDocuments(f.instance.firestoreCollection.documentAccessor(), {
3732
+ firestoreIteration = f.instance.mockItemCollection.firestoreIteration;
3733
+ items = await makeDocuments(f.instance.mockItemCollection.documentAccessor(), {
2926
3734
  count: testDocumentCount,
2927
3735
  init: (i) => {
2928
3736
  return {
@@ -3800,4 +4608,4 @@ function describeFirebaseStorageAccessorDriverTests(f) {
3800
4608
  });
3801
4609
  }
3802
4610
 
3803
- export { MOCK_FIREBASE_MODEL_SERVICE_FACTORIES, MOCK_SYSTEM_STATE_TYPE, MockItemCollectionFixture, MockItemCollectionFixtureInstance, MockItemCollections, MockItemDocument, MockItemPrivateDocument, MockItemSettingsItemEnum, MockItemStorageFixture, MockItemStorageFixtureInstance, MockItemSubItemDeepDocument, MockItemSubItemDocument, MockItemUserDocument, RulesUnitTestFirebaseTestingContextFixture, RulesUnitTestTestFirebaseInstance, TESTING_AUTHORIZED_FIREBASE_USER_ID, TestFirebaseContextFixture, TestFirebaseInstance, TestFirebaseStorageContextFixture, TestFirebaseStorageInstance, TestFirestoreContextFixture, TestFirestoreInstance, allChildMockItemSubItemDeepsWithinMockItem, authorizedFirebaseFactory, authorizedTestWithMockItemCollection, authorizedTestWithMockItemStorage, changeFirestoreLogLevelBeforeAndAfterTests, clearTestFirestoreContextCollections, describeFirebaseStorageAccessorDriverTests, describeFirestoreAccessorDriverTests, describeFirestoreDocumentAccessorTests, describeFirestoreIterationTests, describeFirestoreQueryDriverTests, firebaseRulesUnitTestBuilder, makeMockItemCollections, makeRulesTestFirebaseStorageContext, makeRulesTestFirestoreContext, makeTestingFirebaseStorageAccesorDriver, makeTestingFirebaseStorageDrivers, makeTestingFirestoreAccesorDriver, makeTestingFirestoreDrivers, mockFirebaseModelServices, mockItemCollectionReference, mockItemConverter, mockItemFirebaseModelServiceFactory, mockItemFirestoreCollection, mockItemIdentity, mockItemPrivateCollectionReference, mockItemPrivateCollectionReferenceFactory, mockItemPrivateConverter, mockItemPrivateFirebaseModelServiceFactory, mockItemPrivateFirestoreCollection, mockItemPrivateFirestoreCollectionGroup, mockItemPrivateIdentity, mockItemSettingsItemDencoder, mockItemSubItemCollectionReference, mockItemSubItemCollectionReferenceFactory, mockItemSubItemConverter, mockItemSubItemDeepCollectionReference, mockItemSubItemDeepCollectionReferenceFactory, mockItemSubItemDeepConverter, mockItemSubItemDeepFirebaseModelServiceFactory, mockItemSubItemDeepFirestoreCollection, mockItemSubItemDeepFirestoreCollectionGroup, mockItemSubItemDeepIdentity, mockItemSubItemFirebaseModelServiceFactory, mockItemSubItemFirestoreCollection, mockItemSubItemFirestoreCollectionGroup, mockItemSubItemIdentity, mockItemSystemDataConverter, mockItemSystemStateFirebaseModelServiceFactory, mockItemSystemStateStoredDataConverterMap, mockItemUserAccessorFactory, mockItemUserCollectionName, mockItemUserCollectionReference, mockItemUserCollectionReferenceFactory, mockItemUserConverter, mockItemUserFirebaseModelServiceFactory, mockItemUserFirestoreCollection, mockItemUserFirestoreCollectionGroup, mockItemUserIdentifier, mockItemUserIdentity, mockItemWithTestValue, mockItemWithValue, testWithMockItemCollectionFixture, testWithMockItemStorageFixture };
4611
+ export { MOCK_FIREBASE_MODEL_SERVICE_FACTORIES, MOCK_SYSTEM_STATE_TYPE, MockItemCollectionFixture, MockItemCollectionFixtureInstance, MockItemCollections, MockItemDocument, MockItemPrivateDocument, MockItemSettingsItemEnum, MockItemStorageFixture, MockItemStorageFixtureInstance, MockItemSubItemDeepDocument, MockItemSubItemDocument, MockItemUserDocument, RulesUnitTestFirebaseTestingContextFixture, RulesUnitTestTestFirebaseInstance, TESTING_AUTHORIZED_FIREBASE_USER_ID, TestFirebaseContextFixture, TestFirebaseInstance, TestFirebaseStorageContextFixture, TestFirebaseStorageInstance, TestFirestoreContextFixture, TestFirestoreInstance, allChildMockItemSubItemDeepsWithinMockItem, authorizedFirebaseFactory, authorizedTestWithMockItemCollection, authorizedTestWithMockItemStorage, changeFirestoreLogLevelBeforeAndAfterTests, clearTestFirestoreContextCollections, describeFirebaseStorageAccessorDriverTests, describeFirestoreAccessorDriverTests, describeFirestoreDocumentAccessorTests, describeFirestoreDocumentUtilityTests, describeFirestoreIterationTests, describeFirestoreQueryDriverTests, firebaseRulesUnitTestBuilder, makeMockItemCollections, makeRulesTestFirebaseStorageContext, makeRulesTestFirestoreContext, makeTestingFirebaseStorageAccesorDriver, makeTestingFirebaseStorageDrivers, makeTestingFirestoreAccesorDriver, makeTestingFirestoreDrivers, mockFirebaseModelServices, mockItemCollectionReference, mockItemConverter, mockItemFirebaseModelServiceFactory, mockItemFirestoreCollection, mockItemIdentity, mockItemPrivateCollectionReference, mockItemPrivateCollectionReferenceFactory, mockItemPrivateConverter, mockItemPrivateFirebaseModelServiceFactory, mockItemPrivateFirestoreCollection, mockItemPrivateFirestoreCollectionGroup, mockItemPrivateIdentity, mockItemSettingsItemDencoder, mockItemSubItemCollectionReference, mockItemSubItemCollectionReferenceFactory, mockItemSubItemConverter, mockItemSubItemDeepCollectionReference, mockItemSubItemDeepCollectionReferenceFactory, mockItemSubItemDeepConverter, mockItemSubItemDeepFirebaseModelServiceFactory, mockItemSubItemDeepFirestoreCollection, mockItemSubItemDeepFirestoreCollectionGroup, mockItemSubItemDeepIdentity, mockItemSubItemFirebaseModelServiceFactory, mockItemSubItemFirestoreCollection, mockItemSubItemFirestoreCollectionGroup, mockItemSubItemIdentity, mockItemSystemDataConverter, mockItemSystemStateFirebaseModelServiceFactory, mockItemSystemStateStoredDataConverterMap, mockItemUserAccessorFactory, mockItemUserCollectionName, mockItemUserCollectionReference, mockItemUserCollectionReferenceFactory, mockItemUserConverter, mockItemUserFirebaseModelServiceFactory, mockItemUserFirestoreCollection, mockItemUserFirestoreCollectionGroup, mockItemUserIdentifier, mockItemUserIdentity, mockItemWithTestValue, mockItemWithValue, testWithMockItemCollectionFixture, testWithMockItemStorageFixture };