17
17
const duplexify = require ( 'duplexify' ) ;
18
18
19
19
import { expect } from 'chai' ;
20
+ import { CallOptions } from 'google-gax' ;
20
21
import * as path from 'path' ;
21
22
import * as protobufjs from 'protobufjs' ;
22
23
import * as through2 from 'through2' ;
23
24
import * as proto from '../protos/firestore_proto_api' ;
24
25
25
- import * as Firestore from '../src' ;
26
- import { documentFromJson } from '../src/convert' ;
26
+ import { DocumentChange , DocumentSnapshot , FieldPath , FieldValue , Firestore , Query , QueryDocumentSnapshot , QuerySnapshot , Timestamp } from '../src' ;
27
+ import { fieldsFromJson } from '../src/convert' ;
27
28
import { DocumentChangeType } from '../src/document-change' ;
28
29
import { ResourcePath } from '../src/path' ;
30
+ import { DocumentData } from '../src/types' ;
29
31
import { isObject } from '../src/util' ;
30
- import { createInstance as createInstanceHelper } from '../test/util/helpers' ;
32
+ import { ApiOverride , createInstance as createInstanceHelper } from '../test/util/helpers' ;
31
33
32
34
import api = proto . google . firestore . v1 ;
33
35
36
+ // TODO(mrschmidt): Create Protobuf .d.ts file for the conformance proto
37
+ type ConformanceProto = any ; // tslint:disable-line:no-any
38
+
34
39
const REQUEST_TIME = 'REQUEST_TIME' ;
35
40
36
41
/** List of test cases that are ignored. */
@@ -60,14 +65,14 @@ const COMMIT_REQUEST_TYPE =
60
65
protoDefinition . lookupType ( 'google.firestore.v1.CommitRequest' ) ;
61
66
62
67
// Firestore instance initialized by the test runner.
63
- let firestore ;
68
+ let firestore : Firestore ;
64
69
65
- const docRef = path => {
70
+ const docRef = ( path : string ) => {
66
71
const relativePath = ResourcePath . fromSlashSeparatedString ( path ) . relativeName ;
67
72
return firestore . doc ( relativePath ) ;
68
73
} ;
69
74
70
- const collRef = path => {
75
+ const collRef = ( path : string ) => {
71
76
const relativePath = ResourcePath . fromSlashSeparatedString ( path ) . relativeName ;
72
77
return firestore . collection ( relativePath ) ;
73
78
} ;
@@ -76,7 +81,7 @@ const watchQuery = () => {
76
81
return firestore . collection ( 'C' ) . orderBy ( 'a' ) ;
77
82
} ;
78
83
79
- const createInstance = overrides => {
84
+ const createInstance = ( overrides : ApiOverride ) => {
80
85
return createInstanceHelper (
81
86
overrides , { projectId : CONFORMANCE_TEST_PROJECT_ID } )
82
87
. then ( firestoreClient => {
@@ -87,36 +92,38 @@ const createInstance = overrides => {
87
92
88
93
/** Converts JSON test data into JavaScript types suitable for the Node API. */
89
94
const convertInput = {
90
- argument : json => {
95
+ argument : ( json : ConformanceProto ) => {
91
96
const obj = JSON . parse ( json ) ;
92
- function convertValue ( value ) {
97
+ function convertValue ( value : unknown ) {
93
98
if ( isObject ( value ) ) {
94
99
return convertObject ( value ) ;
95
100
} else if ( Array . isArray ( value ) ) {
96
101
return convertArray ( value ) ;
97
102
} else if ( value === 'NaN' ) {
98
103
return NaN ;
99
104
} else if ( value === 'Delete' ) {
100
- return Firestore . FieldValue . delete ( ) ;
105
+ return FieldValue . delete ( ) ;
101
106
} else if ( value === 'ServerTimestamp' ) {
102
- return Firestore . FieldValue . serverTimestamp ( ) ;
107
+ return FieldValue . serverTimestamp ( ) ;
103
108
}
104
109
105
110
return value ;
106
111
}
107
- function convertArray ( arr ) {
112
+ function convertArray ( arr : Array < unknown > ) : Array < unknown > | FieldValue {
108
113
if ( arr . length > 0 && arr [ 0 ] === 'ArrayUnion' ) {
109
- return Firestore . FieldValue . arrayUnion ( ...convertArray ( arr . slice ( 1 ) ) ) ;
114
+ return FieldValue . arrayUnion (
115
+ ...( convertArray ( arr . slice ( 1 ) ) as Array < unknown > ) ) ;
110
116
} else if ( arr . length > 0 && arr [ 0 ] === 'ArrayRemove' ) {
111
- return Firestore . FieldValue . arrayRemove ( ...convertArray ( arr . slice ( 1 ) ) ) ;
117
+ return FieldValue . arrayRemove (
118
+ ...( convertArray ( arr . slice ( 1 ) ) as Array < unknown > ) ) ;
112
119
} else {
113
120
for ( let i = 0 ; i < arr . length ; ++ i ) {
114
121
arr [ i ] = convertValue ( arr [ i ] ) ;
115
122
}
116
123
return arr ;
117
124
}
118
125
}
119
- function convertObject ( obj ) {
126
+ function convertObject ( obj : { [ k : string ] : unknown } ) {
120
127
for ( const key in obj ) {
121
128
if ( obj . hasOwnProperty ( key ) ) {
122
129
obj [ key ] = convertValue ( obj [ key ] ) ;
@@ -126,94 +133,98 @@ const convertInput = {
126
133
}
127
134
return convertValue ( obj ) ;
128
135
} ,
129
- precondition : precondition => {
136
+ precondition : ( precondition : string ) => {
130
137
const deepCopy = JSON . parse ( JSON . stringify ( precondition ) ) ;
131
138
if ( deepCopy . updateTime ) {
132
- deepCopy . lastUpdateTime =
133
- Firestore . Timestamp . fromProto ( deepCopy . updateTime ) ;
139
+ deepCopy . lastUpdateTime = Timestamp . fromProto ( deepCopy . updateTime ) ;
134
140
delete deepCopy . updateTime ;
135
141
}
136
142
return deepCopy ;
137
143
} ,
138
- path : path => {
144
+ path : ( path : ConformanceProto ) => {
139
145
if ( path . field . length === 1 && path . field [ 0 ] === '__name__' ) {
140
- return Firestore . FieldPath . documentId ( ) ;
146
+ return FieldPath . documentId ( ) ;
141
147
}
142
- return new Firestore . FieldPath ( path . field ) ;
148
+ return new FieldPath ( path . field ) ;
143
149
} ,
144
- paths : fields => {
145
- const convertedPaths : Array < { } > = [ ] ;
150
+ paths : ( fields : ConformanceProto ) : FieldPath [ ] => {
151
+ const convertedPaths : FieldPath [ ] = [ ] ;
146
152
if ( fields ) {
147
153
for ( const field of fields ) {
148
154
convertedPaths . push ( convertInput . path ( field ) ) ;
149
155
}
150
156
} else {
151
- convertedPaths . push ( Firestore . FieldPath . documentId ( ) ) ;
157
+ convertedPaths . push ( FieldPath . documentId ( ) ) ;
152
158
}
153
159
return convertedPaths ;
154
160
} ,
155
- cursor : cursor => {
156
- const args : Array < { } > = [ ] ;
161
+ cursor : ( cursor : ConformanceProto ) => {
162
+ const args : Array < unknown > = [ ] ;
157
163
if ( cursor . docSnapshot ) {
158
- args . push ( Firestore . DocumentSnapshot . fromObject (
164
+ args . push ( DocumentSnapshot . fromObject (
159
165
docRef ( cursor . docSnapshot . path ) ,
160
- convertInput . argument ( cursor . docSnapshot . jsonData ) ) ) ;
166
+ convertInput . argument ( cursor . docSnapshot . jsonData ) as DocumentData ) ) ;
161
167
} else {
162
168
for ( const jsonValue of cursor . jsonValues ) {
163
169
args . push ( convertInput . argument ( jsonValue ) ) ;
164
170
}
165
171
}
166
172
return args ;
167
173
} ,
168
- snapshot : snapshot => {
169
- const docs : Firestore . QueryDocumentSnapshot [ ] = [ ] ;
170
- const changes : Firestore . DocumentChange [ ] = [ ] ;
171
- const readTime = Firestore . Timestamp . fromProto ( snapshot . readTime ) ;
174
+ snapshot : ( snapshot : ConformanceProto ) => {
175
+ const docs : QueryDocumentSnapshot [ ] = [ ] ;
176
+ const changes : DocumentChange [ ] = [ ] ;
177
+ const readTime = Timestamp . fromProto ( snapshot . readTime ) ;
172
178
173
179
for ( const doc of snapshot . docs ) {
174
180
const deepCopy = JSON . parse ( JSON . stringify ( doc ) ) ;
175
- deepCopy . fields = documentFromJson ( deepCopy . fields ) ;
181
+ deepCopy . fields = fieldsFromJson ( deepCopy . fields ) ;
176
182
docs . push (
177
- firestore . snapshot_ ( deepCopy , readTime , 'json' ) as
178
- Firestore . QueryDocumentSnapshot ) ;
183
+ firestore . snapshot_ (
184
+ deepCopy , readTime . toDate ( ) . toISOString ( ) , 'json' ) as
185
+ QueryDocumentSnapshot ) ;
179
186
}
180
187
181
188
for ( const change of snapshot . changes ) {
182
189
const deepCopy = JSON . parse ( JSON . stringify ( change . doc ) ) ;
183
- deepCopy . fields = documentFromJson ( deepCopy . fields ) ;
184
- const doc = firestore . snapshot_ ( deepCopy , readTime , 'json' ) ;
190
+ deepCopy . fields = fieldsFromJson ( deepCopy . fields ) ;
191
+ const doc = firestore . snapshot_ (
192
+ deepCopy , readTime . toDate ( ) . toISOString ( ) , 'json' ) as
193
+ QueryDocumentSnapshot ;
185
194
const type =
186
195
( [ 'unspecified' , 'added' , 'removed' , 'modified' ] [ change . kind ] as
187
196
DocumentChangeType ) ;
188
- changes . push ( new Firestore . DocumentChange (
189
- type , doc , change . oldIndex , change . newIndex ) ) ;
197
+ changes . push (
198
+ new DocumentChange ( type , doc , change . oldIndex , change . newIndex ) ) ;
190
199
}
191
200
192
- return new Firestore . QuerySnapshot (
201
+ return new QuerySnapshot (
193
202
watchQuery ( ) , readTime , docs . length , ( ) => docs , ( ) => changes ) ;
194
203
} ,
195
204
} ;
196
205
197
206
/** Converts Firestore Protobuf types in Proto3 JSON format to Protobuf JS. */
198
207
const convertProto = {
199
- targetChange : type => type || 'NO_CHANGE' ,
200
- listenResponse : listenRequest => {
208
+ targetChange : ( type ?: string ) => type || 'NO_CHANGE' ,
209
+ listenResponse : ( listenRequest : ConformanceProto ) => {
201
210
const deepCopy = JSON . parse ( JSON . stringify ( listenRequest ) ) ;
202
211
if ( deepCopy . targetChange ) {
203
212
deepCopy . targetChange . targetChangeType =
204
213
convertProto . targetChange ( deepCopy . targetChange . targetChangeType ) ;
205
214
}
206
215
if ( deepCopy . documentChange ) {
207
216
deepCopy . documentChange . document . fields =
208
- documentFromJson ( deepCopy . documentChange . document . fields ) ;
217
+ fieldsFromJson ( deepCopy . documentChange . document . fields ) ;
209
218
}
210
219
return deepCopy ;
211
220
} ,
212
221
} ;
213
222
214
223
/** Request handler for _commit. */
215
- function commitHandler ( spec ) {
216
- return ( request , options , callback ) => {
224
+ function commitHandler ( spec : ConformanceProto ) {
225
+ return ( request : api . ICommitRequest , options : CallOptions ,
226
+ callback : ( err : Error | null | undefined , resp ?: api . ICommitResponse ) =>
227
+ void ) => {
217
228
try {
218
229
const actualCommit = COMMIT_REQUEST_TYPE . fromObject ( request ) ;
219
230
const expectedCommit = COMMIT_REQUEST_TYPE . fromObject ( spec . request ) ;
@@ -222,7 +233,7 @@ function commitHandler(spec) {
222
233
commitTime : { } ,
223
234
writeResults : [ ] ,
224
235
} ;
225
- for ( let i = 1 ; i <= request . writes . length ; ++ i ) {
236
+ for ( let i = 1 ; i <= request . writes ! . length ; ++ i ) {
226
237
res . writeResults ! . push ( {
227
238
updateTime : { } ,
228
239
} ) ;
@@ -235,10 +246,10 @@ function commitHandler(spec) {
235
246
}
236
247
237
248
/** Request handler for _runQuery. */
238
- function queryHandler ( spec ) {
239
- return request => {
249
+ function queryHandler ( spec : ConformanceProto ) {
250
+ return ( request : api . IRunQueryRequest ) => {
240
251
const actualQuery =
241
- STRUCTURED_QUERY_TYPE . fromObject ( request . structuredQuery ) ;
252
+ STRUCTURED_QUERY_TYPE . fromObject ( request . structuredQuery ! ) ;
242
253
const expectedQuery = STRUCTURED_QUERY_TYPE . fromObject ( spec . query ) ;
243
254
expect ( actualQuery ) . to . deep . equal ( expectedQuery ) ;
244
255
const stream = through2 . obj ( ) ;
@@ -248,10 +259,10 @@ function queryHandler(spec) {
248
259
}
249
260
250
261
/** Request handler for _batchGetDocuments. */
251
- function getHandler ( spec ) {
252
- return request => {
262
+ function getHandler ( spec : ConformanceProto ) {
263
+ return ( request : api . IBatchGetDocumentsRequest ) => {
253
264
const getDocument = spec . request ;
254
- expect ( request . documents [ 0 ] ) . to . equal ( getDocument . name ) ;
265
+ expect ( request . documents ! [ 0 ] ) . to . equal ( getDocument . name ) ;
255
266
const stream = through2 . obj ( ) ;
256
267
setImmediate ( ( ) => {
257
268
stream . push ( {
@@ -264,19 +275,19 @@ function getHandler(spec) {
264
275
} ;
265
276
}
266
277
267
- function runTest ( spec ) {
278
+ function runTest ( spec : ConformanceProto ) {
268
279
console . log ( `Running Spec:\n${ JSON . stringify ( spec , null , 2 ) } \n` ) ;
269
280
270
- const updateTest = spec => {
281
+ const updateTest = ( spec : ConformanceProto ) => {
271
282
const overrides = { commit : commitHandler ( spec ) } ;
272
283
return createInstance ( overrides ) . then ( ( ) => {
273
- const varargs : Array < { } > = [ ] ;
284
+ const varargs : Array < unknown > = [ ] ;
274
285
275
286
if ( spec . jsonData ) {
276
287
varargs [ 0 ] = convertInput . argument ( spec . jsonData ) ;
277
288
} else {
278
289
for ( let i = 0 ; i < spec . fieldPaths . length ; ++ i ) {
279
- varargs [ 2 * i ] = new Firestore . FieldPath ( spec . fieldPaths [ i ] . field ) ;
290
+ varargs [ 2 * i ] = new FieldPath ( spec . fieldPaths [ i ] . field ) ;
280
291
}
281
292
for ( let i = 0 ; i < spec . jsonValues . length ; ++ i ) {
282
293
varargs [ 2 * i + 1 ] = convertInput . argument ( spec . jsonValues [ i ] ) ;
@@ -288,13 +299,16 @@ function runTest(spec) {
288
299
}
289
300
290
301
const document = docRef ( spec . docRefPath ) ;
291
- return document . update . apply ( document , varargs ) ;
302
+ // TODO(mrschmidt): Remove 'any' and invoke by calling update() directly
303
+ // for each individual case.
304
+ // tslint:disable-next-line:no-any
305
+ return document . update . apply ( document , varargs as any ) ;
292
306
} ) ;
293
307
} ;
294
308
295
- const queryTest = spec => {
309
+ const queryTest = ( spec : ConformanceProto ) => {
296
310
const overrides = { runQuery : queryHandler ( spec ) } ;
297
- const applyClause = ( query , clause ) => {
311
+ const applyClause = ( query : Query , clause : ConformanceProto ) => {
298
312
if ( clause . select ) {
299
313
query =
300
314
query . select . apply ( query , convertInput . paths ( clause . select . fields ) ) ;
@@ -325,15 +339,15 @@ function runTest(spec) {
325
339
} ;
326
340
327
341
return createInstance ( overrides ) . then ( ( ) => {
328
- let query = collRef ( spec . collPath ) ;
342
+ let query : Query = collRef ( spec . collPath ) ;
329
343
for ( const clause of spec . clauses ) {
330
344
query = applyClause ( query , clause ) ;
331
345
}
332
346
return query . get ( ) ;
333
347
} ) ;
334
348
} ;
335
349
336
- const deleteTest = spec => {
350
+ const deleteTest = ( spec : ConformanceProto ) => {
337
351
const overrides = { commit : commitHandler ( spec ) } ;
338
352
return createInstance ( overrides ) . then ( ( ) => {
339
353
if ( spec . precondition ) {
@@ -345,43 +359,42 @@ function runTest(spec) {
345
359
} ) ;
346
360
} ;
347
361
348
- const setTest = spec => {
362
+ const setTest = ( spec : ConformanceProto ) => {
349
363
const overrides = { commit : commitHandler ( spec ) } ;
350
364
return createInstance ( overrides ) . then ( ( ) => {
351
- const setOption : { merge ?: boolean ,
352
- mergeFields ?: Firestore . FieldPath [ ] } = { } ;
365
+ const setOption : { merge ?: boolean , mergeFields ?: FieldPath [ ] } = { } ;
353
366
if ( spec . option && spec . option . all ) {
354
367
setOption . merge = true ;
355
368
} else if ( spec . option && spec . option . fields ) {
356
369
setOption . mergeFields = [ ] ;
357
370
for ( const fieldPath of spec . option . fields ) {
358
- setOption . mergeFields . push ( new Firestore . FieldPath ( fieldPath . field ) ) ;
371
+ setOption . mergeFields . push ( new FieldPath ( fieldPath . field ) ) ;
359
372
}
360
373
}
361
374
return docRef ( setSpec . docRefPath )
362
- . set ( convertInput . argument ( spec . jsonData ) , setOption ) ;
375
+ . set ( convertInput . argument ( spec . jsonData ) as DocumentData , setOption ) ;
363
376
} ) ;
364
377
} ;
365
378
366
- const createTest = spec => {
379
+ const createTest = ( spec : ConformanceProto ) => {
367
380
const overrides = { commit : commitHandler ( spec ) } ;
368
381
return createInstance ( overrides ) . then ( ( ) => {
369
382
return docRef ( spec . docRefPath )
370
- . create ( convertInput . argument ( spec . jsonData ) ) ;
383
+ . create ( convertInput . argument ( spec . jsonData ) as DocumentData ) ;
371
384
} ) ;
372
385
} ;
373
386
374
- const getTest = spec => {
387
+ const getTest = ( spec : ConformanceProto ) => {
375
388
const overrides = { batchGetDocuments : getHandler ( spec ) } ;
376
389
return createInstance ( overrides ) . then ( ( ) => {
377
390
return docRef ( spec . docRefPath ) . get ( ) ;
378
391
} ) ;
379
392
} ;
380
393
381
- const watchTest = spec => {
394
+ const watchTest = ( spec : ConformanceProto ) => {
382
395
const expectedSnapshots = spec . snapshots ;
383
396
const writeStream = through2 . obj ( ) ;
384
- const overrides = {
397
+ const overrides : ApiOverride = {
385
398
listen : ( ) => duplexify . obj ( through2 . obj ( ) , writeStream )
386
399
} ;
387
400
@@ -418,7 +431,7 @@ function runTest(spec) {
418
431
} ) ;
419
432
} ;
420
433
421
- let testSpec ;
434
+ let testSpec : ConformanceProto ;
422
435
let testPromise ;
423
436
424
437
const getSpec = spec . get ;
0 commit comments