mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
178 lines
5.6 KiB
TypeScript
178 lines
5.6 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2019 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import {
|
|
newQueryForCollectionGroup,
|
|
newQueryForPath,
|
|
queryWithAddedFilter
|
|
} from '../../../src/core/query';
|
|
import { Document } from '../../../src/model/document';
|
|
import { doc, filter, query } from '../../util/helpers';
|
|
|
|
import { describeSpec, specTest } from './describe_spec';
|
|
import { client, spec, SpecBuilder } from './spec_builder';
|
|
|
|
// Helper to seed the cache with the specified docs by listening to each one.
|
|
function specWithCachedDocs(...docs: Document[]): SpecBuilder {
|
|
let builder = spec();
|
|
for (const doc of docs) {
|
|
const query1 = newQueryForPath(doc.key.path);
|
|
builder = builder
|
|
.userListens(query1)
|
|
.watchAcksFull(query1, 1000, doc)
|
|
.expectEvents(query1, { added: [doc] });
|
|
}
|
|
return builder;
|
|
}
|
|
|
|
describeSpec('Queries:', [], () => {
|
|
specTest('Collection Group query', [], () => {
|
|
const cgQuery = newQueryForCollectionGroup('cg');
|
|
const cgQueryWithFilter = queryWithAddedFilter(
|
|
cgQuery,
|
|
filter('val', '==', 1)
|
|
);
|
|
const docs = [
|
|
doc('cg/1', 1000, { val: 1 }),
|
|
doc('cg/2', 1000, { val: 2 }),
|
|
doc('not-cg/nope/cg/3', 1000, { val: 1 }),
|
|
doc('not-cg/nope', 1000, { val: 1 }),
|
|
doc('cg/1/not-cg/nope', 1000, { val: 1 })
|
|
];
|
|
return specWithCachedDocs(...docs)
|
|
.userListens(cgQuery)
|
|
.expectEvents(cgQuery, {
|
|
added: docs.slice(0, 3), // first 3 docs match,
|
|
fromCache: true
|
|
})
|
|
.userListens(cgQueryWithFilter)
|
|
.expectEvents(cgQueryWithFilter, {
|
|
added: [docs[0], docs[2]],
|
|
fromCache: true
|
|
});
|
|
});
|
|
|
|
specTest('Collection Group query with mutations', [], () => {
|
|
const cgQuery = newQueryForCollectionGroup('cg');
|
|
const cgQueryWithFilter = queryWithAddedFilter(
|
|
cgQuery,
|
|
filter('val', '==', 1)
|
|
);
|
|
const cachedDocs = [
|
|
doc('cg/1', 1000, { val: 1 }),
|
|
doc('not-cg/nope', 1000, { val: 1 })
|
|
];
|
|
const toWrite1 = doc('cg/2', 0, { val: 2 }).setHasLocalMutations();
|
|
const toWrite2 = doc('not-cg/nope/cg/3', 0, {
|
|
val: 1
|
|
}).setHasLocalMutations();
|
|
const toWrite3 = doc('not-cg2/nope', 0, { val: 1 }).setHasLocalMutations();
|
|
|
|
return specWithCachedDocs(...cachedDocs)
|
|
.userSets(toWrite1.key.toString(), { val: 2 })
|
|
.userSets(toWrite2.key.toString(), { val: 1 })
|
|
.userSets(toWrite3.key.toString(), { val: 1 })
|
|
.userListens(cgQuery)
|
|
.expectEvents(cgQuery, {
|
|
added: [cachedDocs[0], toWrite1, toWrite2],
|
|
fromCache: true,
|
|
hasPendingWrites: true
|
|
})
|
|
.userListens(cgQueryWithFilter)
|
|
.expectEvents(cgQueryWithFilter, {
|
|
added: [cachedDocs[0], toWrite2],
|
|
fromCache: true,
|
|
hasPendingWrites: true
|
|
});
|
|
});
|
|
|
|
specTest(
|
|
'Latency-compensated updates are included in query results',
|
|
[],
|
|
() => {
|
|
const fullQuery = query('collection');
|
|
const filteredQuery = queryWithAddedFilter(
|
|
fullQuery,
|
|
filter('match', '==', true)
|
|
);
|
|
const docA = doc('collection/a', 1000, { match: false });
|
|
const docAv2Local = doc('collection/a', 1000, {
|
|
match: true
|
|
}).setHasLocalMutations();
|
|
|
|
return (
|
|
spec()
|
|
.userListens(fullQuery)
|
|
.watchAcksFull(fullQuery, 1000, docA)
|
|
.expectEvents(fullQuery, { added: [docA] })
|
|
|
|
// patch docA so that it will now match the filtered query.
|
|
.userPatches('collection/a', { match: true })
|
|
.expectEvents(fullQuery, {
|
|
modified: [docAv2Local],
|
|
hasPendingWrites: true
|
|
})
|
|
// Make sure docA shows up in filtered query.
|
|
.userListens(filteredQuery)
|
|
.expectEvents(filteredQuery, {
|
|
added: [docAv2Local],
|
|
fromCache: true,
|
|
hasPendingWrites: true
|
|
})
|
|
);
|
|
}
|
|
);
|
|
|
|
specTest(
|
|
'Queries in different tabs will not interfere',
|
|
['multi-client'],
|
|
() => {
|
|
const query1 = query('collection', filter('key', '==', 'a'));
|
|
const query2 = query('collection', filter('key', '==', 'b'));
|
|
const docA = doc('collection/a', 1000, { key: 'a' });
|
|
const docB = doc('collection/b', 1000, { key: 'b' });
|
|
|
|
return (
|
|
client(0)
|
|
.becomeVisible()
|
|
// Listen to the first query in the primary client
|
|
.expectPrimaryState(true)
|
|
.userListens(query1)
|
|
.watchAcks(query1)
|
|
.watchSends({ affects: [query1] }, docA)
|
|
|
|
// Listen to different query in the secondary client
|
|
.client(1)
|
|
.userListens(query2)
|
|
|
|
.client(0)
|
|
.expectListen(query2)
|
|
.watchCurrents(query1, 'resume-token-1000')
|
|
// Receive global snapshot before the second query is acknowledged
|
|
.watchSnapshots(1000)
|
|
.expectEvents(query1, { added: [docA] })
|
|
// This should not trigger empty snapshot for second query(bugged behavior)
|
|
.client(1)
|
|
.client(0)
|
|
.watchAcksFull(query2, 2000, docB)
|
|
.client(1)
|
|
.expectEvents(query2, { added: [docB] })
|
|
);
|
|
}
|
|
);
|
|
});
|