mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
452 lines
13 KiB
TypeScript
452 lines
13 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2017 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 { expect } from 'chai';
|
|
|
|
import {
|
|
FieldPath,
|
|
getDoc,
|
|
getDocs,
|
|
orderBy,
|
|
query,
|
|
setDoc,
|
|
startAt,
|
|
Timestamp,
|
|
updateDoc,
|
|
where
|
|
} from '../util/firebase_export';
|
|
import {
|
|
apiDescribe,
|
|
toDataArray,
|
|
withTestCollection,
|
|
withTestCollectionSettings,
|
|
withTestDoc,
|
|
withTestDocAndSettings
|
|
} from '../util/helpers';
|
|
import { DEFAULT_SETTINGS } from '../util/settings';
|
|
|
|
// Allow custom types for testing.
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
type AnyTestData = any;
|
|
|
|
apiDescribe('Nested Fields', persistence => {
|
|
const testData = (n?: number): AnyTestData => {
|
|
n = n || 1;
|
|
return {
|
|
name: 'room ' + n,
|
|
metadata: {
|
|
createdAt: n,
|
|
deep: {
|
|
field: 'deep-field-' + n
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
it('can be written with set()', () => {
|
|
return withTestDoc(persistence, doc => {
|
|
return setDoc(doc, testData())
|
|
.then(() => getDoc(doc))
|
|
.then(docSnap => {
|
|
expect(docSnap.data()).to.deep.equal(testData());
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can be read directly with .get(<string>)', () => {
|
|
return withTestDoc(persistence, doc => {
|
|
const obj = testData();
|
|
return setDoc(doc, obj)
|
|
.then(() => getDoc(doc))
|
|
.then(docSnap => {
|
|
expect(docSnap.data()).to.deep.equal(obj);
|
|
expect(docSnap.get('name')).to.deep.equal(obj.name);
|
|
expect(docSnap.get('metadata')).to.deep.equal(obj.metadata);
|
|
expect(docSnap.get('metadata.deep.field')).to.deep.equal(
|
|
obj.metadata.deep.field
|
|
);
|
|
expect(docSnap.get('metadata.nofield')).to.be.undefined;
|
|
expect(docSnap.get('nometadata.nofield')).to.be.undefined;
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can be read directly with .get(<FieldPath>)', () => {
|
|
return withTestDoc(persistence, doc => {
|
|
const obj = testData();
|
|
return setDoc(doc, obj)
|
|
.then(() => getDoc(doc))
|
|
.then(docSnap => {
|
|
expect(docSnap.data()).to.deep.equal(obj);
|
|
expect(docSnap.get(new FieldPath('name'))).to.deep.equal(obj.name);
|
|
expect(docSnap.get(new FieldPath('metadata'))).to.deep.equal(
|
|
obj.metadata
|
|
);
|
|
expect(
|
|
docSnap.get(new FieldPath('metadata', 'deep', 'field'))
|
|
).to.deep.equal(obj.metadata.deep.field);
|
|
expect(docSnap.get(new FieldPath('metadata', 'nofield'))).to.be
|
|
.undefined;
|
|
expect(docSnap.get(new FieldPath('nometadata', 'nofield'))).to.be
|
|
.undefined;
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can be updated with update(<string>)', () => {
|
|
return withTestDoc(persistence, doc => {
|
|
return setDoc(doc, testData())
|
|
.then(() =>
|
|
updateDoc(doc, {
|
|
'metadata.deep.field': 100,
|
|
'metadata.added': 200
|
|
})
|
|
)
|
|
.then(() => getDoc(doc))
|
|
.then(docSnap => {
|
|
expect(docSnap.data()).to.deep.equal({
|
|
name: 'room 1',
|
|
metadata: {
|
|
createdAt: 1,
|
|
deep: {
|
|
field: 100
|
|
},
|
|
added: 200
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can be updated with update(<FieldPath>)', () => {
|
|
return withTestDoc(persistence, doc => {
|
|
return setDoc(doc, testData())
|
|
.then(() =>
|
|
updateDoc(
|
|
doc,
|
|
new FieldPath('metadata', 'deep', 'field'),
|
|
100,
|
|
new FieldPath('metadata', 'added'),
|
|
200
|
|
)
|
|
)
|
|
.then(() => getDoc(doc))
|
|
.then(docSnap => {
|
|
expect(docSnap.data()).to.deep.equal({
|
|
name: 'room 1',
|
|
metadata: {
|
|
createdAt: 1,
|
|
deep: {
|
|
field: 100
|
|
},
|
|
added: 200
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can be used with query.where(<string>).', () => {
|
|
const testDocs = {
|
|
'1': testData(300),
|
|
'2': testData(100),
|
|
'3': testData(200)
|
|
};
|
|
return withTestCollection(persistence, testDocs, coll => {
|
|
return getDocs(query(coll, where('metadata.createdAt', '>=', 200))).then(
|
|
results => {
|
|
// inequality adds implicit sort on field
|
|
expect(toDataArray(results)).to.deep.equal([
|
|
testData(200),
|
|
testData(300)
|
|
]);
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('can be used with query.where(<FieldPath>).', () => {
|
|
const testDocs = {
|
|
'1': testData(300),
|
|
'2': testData(100),
|
|
'3': testData(200)
|
|
};
|
|
return withTestCollection(persistence, testDocs, coll => {
|
|
return getDocs(
|
|
query(coll, where(new FieldPath('metadata', 'createdAt'), '>=', 200))
|
|
).then(results => {
|
|
// inequality adds implicit sort on field
|
|
expect(toDataArray(results)).to.deep.equal([
|
|
testData(200),
|
|
testData(300)
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can be used with query.orderBy(<string>).', () => {
|
|
const testDocs = {
|
|
'1': testData(300),
|
|
'2': testData(100),
|
|
'3': testData(200)
|
|
};
|
|
return withTestCollection(persistence, testDocs, coll => {
|
|
return getDocs(query(coll, orderBy('metadata.createdAt'))).then(
|
|
results => {
|
|
expect(toDataArray(results)).to.deep.equal([
|
|
testData(100),
|
|
testData(200),
|
|
testData(300)
|
|
]);
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('can be used with query.orderBy(<FieldPath>).', () => {
|
|
const testDocs = {
|
|
'1': testData(300),
|
|
'2': testData(100),
|
|
'3': testData(200)
|
|
};
|
|
return withTestCollection(persistence, testDocs, coll => {
|
|
return getDocs(
|
|
query(coll, orderBy(new FieldPath('metadata', 'createdAt')))
|
|
).then(results => {
|
|
expect(toDataArray(results)).to.deep.equal([
|
|
testData(100),
|
|
testData(200),
|
|
testData(300)
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
// NOTE(mikelehen): I originally combined these tests with the above ones, but
|
|
// Datastore currently prohibits having nested fields and fields with dots in
|
|
// the same entity, so I'm separating them.
|
|
apiDescribe('Fields with special characters', persistence => {
|
|
const testData = (n?: number): AnyTestData => {
|
|
n = n || 1;
|
|
return {
|
|
field: 'field ' + n,
|
|
'field.dot': n,
|
|
'field\\slash': n
|
|
};
|
|
};
|
|
|
|
it('can be written with set()', () => {
|
|
return withTestDoc(persistence, doc => {
|
|
return setDoc(doc, testData())
|
|
.then(() => getDoc(doc))
|
|
.then(docSnap => {
|
|
expect(docSnap.data()).to.deep.equal(testData());
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can be read directly with .data(<field>)', () => {
|
|
return withTestDoc(persistence, doc => {
|
|
const obj = testData();
|
|
return setDoc(doc, obj)
|
|
.then(() => getDoc(doc))
|
|
.then(docSnap => {
|
|
expect(docSnap.data()).to.deep.equal(obj);
|
|
expect(docSnap.get(new FieldPath('field.dot'))).to.deep.equal(
|
|
obj['field.dot']
|
|
);
|
|
expect(docSnap.get('field\\slash')).to.deep.equal(
|
|
obj['field\\slash']
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can be updated with update()', () => {
|
|
return withTestDoc(persistence, doc => {
|
|
return setDoc(doc, testData())
|
|
.then(() => {
|
|
return updateDoc(
|
|
doc,
|
|
new FieldPath('field.dot'),
|
|
100,
|
|
'field\\slash',
|
|
200
|
|
);
|
|
})
|
|
.then(() => getDoc(doc))
|
|
.then(docSnap => {
|
|
expect(docSnap.data()).to.deep.equal({
|
|
field: 'field 1',
|
|
'field.dot': 100,
|
|
'field\\slash': 200
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can be used in query filters.', () => {
|
|
const testDocs = {
|
|
'1': testData(300),
|
|
'2': testData(100),
|
|
'3': testData(200)
|
|
};
|
|
return withTestCollection(persistence, testDocs, coll => {
|
|
// inequality adds implicit sort on field
|
|
const expected = [testData(200), testData(300)];
|
|
return getDocs(query(coll, where(new FieldPath('field.dot'), '>=', 200)))
|
|
.then(results => {
|
|
expect(toDataArray(results)).to.deep.equal(expected);
|
|
})
|
|
.then(() => getDocs(query(coll, where('field\\slash', '>=', 200))))
|
|
.then(results => {
|
|
expect(toDataArray(results)).to.deep.equal(expected);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can be used in a query orderBy.', () => {
|
|
const testDocs = {
|
|
'1': testData(300),
|
|
'2': testData(100),
|
|
'3': testData(200)
|
|
};
|
|
return withTestCollection(persistence, testDocs, coll => {
|
|
const expected = [testData(100), testData(200), testData(300)];
|
|
return getDocs(query(coll, orderBy(new FieldPath('field.dot'))))
|
|
.then(results => {
|
|
expect(toDataArray(results)).to.deep.equal(expected);
|
|
})
|
|
.then(() => getDocs(query(coll, orderBy('field\\slash'))))
|
|
.then(results => {
|
|
expect(toDataArray(results)).to.deep.equal(expected);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
apiDescribe('Timestamp Fields in snapshots', persistence => {
|
|
// Figure out how to pass in the Timestamp type
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const testDataWithTimestamps = (ts: any): AnyTestData => {
|
|
return { timestamp: ts, nested: { timestamp2: ts } };
|
|
};
|
|
|
|
it('are returned as Timestamps', () => {
|
|
const timestamp = new Timestamp(100, 123456000);
|
|
// Timestamps are currently truncated to microseconds after being written to
|
|
// the database, so a truncated version of the timestamp is needed for
|
|
// comparisons.
|
|
const truncatedTimestamp = new Timestamp(
|
|
timestamp.seconds,
|
|
Math.floor(timestamp.nanoseconds / 1000) * 1000
|
|
);
|
|
|
|
return withTestDoc(persistence, doc => {
|
|
return setDoc(doc, testDataWithTimestamps(timestamp))
|
|
.then(() => getDoc(doc))
|
|
.then(docSnap => {
|
|
expect(docSnap.get('timestamp'))
|
|
.to.be.an.instanceof(Timestamp)
|
|
.that.deep.equals(truncatedTimestamp);
|
|
expect(docSnap.data()!['timestamp'])
|
|
.to.be.an.instanceof(Timestamp)
|
|
.that.deep.equals(truncatedTimestamp);
|
|
|
|
expect(docSnap.get('nested.timestamp2'))
|
|
.to.be.an.instanceof(Timestamp)
|
|
.that.deep.equals(truncatedTimestamp);
|
|
expect(docSnap.data()!['nested']['timestamp2'])
|
|
.to.be.an.instanceof(Timestamp)
|
|
.that.deep.equals(truncatedTimestamp);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
apiDescribe('`undefined` properties', persistence => {
|
|
const settings = { ...DEFAULT_SETTINGS };
|
|
settings.ignoreUndefinedProperties = true;
|
|
|
|
it('are ignored in set()', () => {
|
|
return withTestDocAndSettings(persistence, settings, async doc => {
|
|
await setDoc(doc, { foo: 'foo', 'bar': undefined });
|
|
const docSnap = await getDoc(doc);
|
|
expect(docSnap.data()).to.deep.equal({ foo: 'foo' });
|
|
});
|
|
});
|
|
|
|
it('are ignored in set({ merge: true })', () => {
|
|
return withTestDocAndSettings(persistence, settings, async doc => {
|
|
await setDoc(doc, { foo: 'foo', bar: 'unchanged' });
|
|
await setDoc(doc, { foo: 'foo', bar: undefined }, { merge: true });
|
|
const docSnap = await getDoc(doc);
|
|
expect(docSnap.data()).to.deep.equal({ foo: 'foo', bar: 'unchanged' });
|
|
});
|
|
});
|
|
|
|
it('are ignored in update()', () => {
|
|
return withTestDocAndSettings(persistence, settings, async doc => {
|
|
await setDoc(doc, {});
|
|
await updateDoc(doc, { a: { foo: 'foo', 'bar': undefined } });
|
|
await updateDoc(doc, 'b', { foo: 'foo', 'bar': undefined });
|
|
const docSnap = await getDoc(doc);
|
|
expect(docSnap.data()).to.deep.equal({
|
|
a: { foo: 'foo' },
|
|
b: { foo: 'foo' }
|
|
});
|
|
});
|
|
});
|
|
|
|
it('are ignored in Query.where()', () => {
|
|
return withTestCollectionSettings(
|
|
persistence,
|
|
settings,
|
|
{ 'doc1': { nested: { foo: 'foo' } } },
|
|
async coll => {
|
|
const filteredQuery = query(
|
|
coll,
|
|
where('nested', '==', {
|
|
foo: 'foo',
|
|
'bar': undefined
|
|
})
|
|
);
|
|
const querySnap = await getDocs(filteredQuery);
|
|
expect(querySnap.size).to.equal(1);
|
|
}
|
|
);
|
|
});
|
|
|
|
it('are ignored in Query.startAt()', () => {
|
|
return withTestCollectionSettings(
|
|
persistence,
|
|
settings,
|
|
{ 'doc1': { nested: { foo: 'foo' } } },
|
|
async coll => {
|
|
const orderedQuery = query(
|
|
coll,
|
|
orderBy('nested'),
|
|
startAt({ foo: 'foo', 'bar': undefined })
|
|
);
|
|
const querySnap = await getDocs(orderedQuery);
|
|
expect(querySnap.size).to.equal(1);
|
|
}
|
|
);
|
|
});
|
|
});
|