firestore-node-mock 0.0.1
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/README.md +479 -0
- package/index.d.ts +6 -0
- package/index.d.ts.map +1 -0
- package/index.js +6 -0
- package/index.js.map +1 -0
- package/mocks/auth.d.ts +34 -0
- package/mocks/auth.js +69 -0
- package/mocks/fieldValue.d.ts +23 -0
- package/mocks/fieldValue.js +57 -0
- package/mocks/firebase.d.ts +37 -0
- package/mocks/firebase.js +79 -0
- package/mocks/firestore.d.ts +181 -0
- package/mocks/firestore.js +610 -0
- package/mocks/googleCloudFirestore.d.ts +20 -0
- package/mocks/googleCloudFirestore.js +43 -0
- package/mocks/helpers/buildDocFromHash.d.ts +28 -0
- package/mocks/helpers/buildDocFromHash.js +73 -0
- package/mocks/helpers/buildQuerySnapShot.d.ts +29 -0
- package/mocks/helpers/buildQuerySnapShot.js +306 -0
- package/mocks/helpers/defaultMockOptions.d.ts +3 -0
- package/mocks/helpers/defaultMockOptions.js +5 -0
- package/mocks/path.d.ts +26 -0
- package/mocks/path.js +39 -0
- package/mocks/query.d.ts +32 -0
- package/mocks/query.js +162 -0
- package/mocks/reactNativeFirebaseFirestore.d.ts +20 -0
- package/mocks/reactNativeFirebaseFirestore.js +43 -0
- package/mocks/timestamp.d.ts +22 -0
- package/mocks/timestamp.js +103 -0
- package/mocks/transaction.d.ts +22 -0
- package/mocks/transaction.js +62 -0
- package/package.json +61 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { mock } from 'node:test';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { FakeFirestore } from './firestore.js';
|
|
4
|
+
import defaultOptions from './helpers/defaultMockOptions.js';
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
|
|
8
|
+
export const firestoreStub = (overrides, options = defaultOptions) => {
|
|
9
|
+
class Firestore extends FakeFirestore {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(overrides.database, options);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
Query: FakeFirestore.Query,
|
|
16
|
+
CollectionReference: FakeFirestore.CollectionReference,
|
|
17
|
+
DocumentReference: FakeFirestore.DocumentReference,
|
|
18
|
+
FieldValue: FakeFirestore.FieldValue,
|
|
19
|
+
FieldPath: FakeFirestore.FieldPath,
|
|
20
|
+
Timestamp: FakeFirestore.Timestamp,
|
|
21
|
+
Transaction: FakeFirestore.Transaction,
|
|
22
|
+
/** @type {Firestore.constructor} */
|
|
23
|
+
Firestore,
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const mockReactNativeFirestore = (overrides = {}, options = defaultOptions) => {
|
|
28
|
+
mockModuleIfFound('@react-native-firebase/firestore', overrides, options);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function mockModuleIfFound(moduleName, overrides, options) {
|
|
32
|
+
try {
|
|
33
|
+
require.resolve(moduleName);
|
|
34
|
+
const stub = firestoreStub(overrides, options);
|
|
35
|
+
mock.module(moduleName, {
|
|
36
|
+
defaultExport: stub,
|
|
37
|
+
namedExports: stub,
|
|
38
|
+
});
|
|
39
|
+
} catch (e) {
|
|
40
|
+
// eslint-disable-next-line no-console
|
|
41
|
+
console.info(`Module ${moduleName} not found, mocking skipped.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Mock } from 'node:test';
|
|
2
|
+
|
|
3
|
+
export class Timestamp {
|
|
4
|
+
constructor(seconds: number, nanoseconds: number);
|
|
5
|
+
|
|
6
|
+
isEqual(other: Timestamp): boolean;
|
|
7
|
+
toDate(): Date;
|
|
8
|
+
toMillis(): number;
|
|
9
|
+
valueOf(): string;
|
|
10
|
+
|
|
11
|
+
static fromDate(date: Date): Timestamp;
|
|
12
|
+
static fromMillis(millis: number): Timestamp;
|
|
13
|
+
static now(): Timestamp;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const mocks: {
|
|
17
|
+
mockTimestampToDate: Mock<any>;
|
|
18
|
+
mockTimestampToMillis: Mock<any>;
|
|
19
|
+
mockTimestampFromDate: Mock<any>;
|
|
20
|
+
mockTimestampFromMillis: Mock<any>;
|
|
21
|
+
mockTimestampNow: Mock<any>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { mock } from 'node:test';
|
|
2
|
+
|
|
3
|
+
export const mockTimestampToDate = mock.fn();
|
|
4
|
+
export const mockTimestampToMillis = mock.fn();
|
|
5
|
+
export const mockTimestampFromDate = mock.fn();
|
|
6
|
+
export const mockTimestampFromMillis = mock.fn();
|
|
7
|
+
export const mockTimestampNow = mock.fn();
|
|
8
|
+
|
|
9
|
+
export class Timestamp {
|
|
10
|
+
constructor(seconds, nanoseconds) {
|
|
11
|
+
this.seconds = seconds;
|
|
12
|
+
this.nanoseconds = nanoseconds;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
isEqual(other) {
|
|
16
|
+
return (
|
|
17
|
+
other instanceof Timestamp &&
|
|
18
|
+
other.seconds === this.seconds &&
|
|
19
|
+
other.nanoseconds === this.nanoseconds
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
toDate() {
|
|
24
|
+
return mockTimestampToDate(...arguments) || new Date(this._toMillis());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
toMillis() {
|
|
28
|
+
return mockTimestampToMillis(...arguments) || this._toMillis();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
valueOf() {
|
|
32
|
+
return JSON.stringify(this.toMillis());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static fromDate(date) {
|
|
36
|
+
return mockTimestampFromDate(...arguments) || Timestamp._fromMillis(date.getTime());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static fromMillis(millis) {
|
|
40
|
+
return mockTimestampFromMillis(...arguments) || Timestamp._fromMillis(millis);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static _fromMillis(millis) {
|
|
44
|
+
const seconds = Math.floor(millis / 1000);
|
|
45
|
+
const nanoseconds = 1000000 * (millis - seconds * 1000);
|
|
46
|
+
return new Timestamp(seconds, nanoseconds);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Dates only return whole-number millis
|
|
50
|
+
_toMillis() {
|
|
51
|
+
return this.seconds * 1000 + Math.round(this.nanoseconds / 1000000);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static now() {
|
|
55
|
+
const now = new Date();
|
|
56
|
+
return mockTimestampNow(...arguments) || Timestamp.fromDate(now);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//
|
|
61
|
+
// Search data for possible timestamps and convert to class.
|
|
62
|
+
export function convertTimestamps(data, path = []) {
|
|
63
|
+
if (!data) {
|
|
64
|
+
return data;
|
|
65
|
+
}
|
|
66
|
+
// we need to avoid self-referencing DB's (can happen on db.get)
|
|
67
|
+
// Check we have not looped. If we have, backout
|
|
68
|
+
if (path.includes(data)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Check if this object is or contains a timestamp
|
|
73
|
+
if (typeof data === 'object') {
|
|
74
|
+
const keys = Object.keys(data);
|
|
75
|
+
// if it is a timestamp, convert to the appropriate class
|
|
76
|
+
if (
|
|
77
|
+
keys.length === 2 &&
|
|
78
|
+
keys.find(k => k === 'seconds') &&
|
|
79
|
+
keys.find(k => k === 'nanoseconds')
|
|
80
|
+
) {
|
|
81
|
+
return new Timestamp(data.seconds, data.nanoseconds);
|
|
82
|
+
} else {
|
|
83
|
+
// Search recursively for any timestamps in this data
|
|
84
|
+
// Keep track of the path taken, so we can avoid self-referencing loops
|
|
85
|
+
// Note: running full-setup.test.js will fail without this check
|
|
86
|
+
// add console.log(`${path} => ${k}`); to see how this class is added as a property
|
|
87
|
+
path.push(data);
|
|
88
|
+
keys.forEach(k => {
|
|
89
|
+
data[k] = convertTimestamps(data[k], path);
|
|
90
|
+
});
|
|
91
|
+
path.pop();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return data;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const mocks = {
|
|
98
|
+
mockTimestampToDate,
|
|
99
|
+
mockTimestampToMillis,
|
|
100
|
+
mockTimestampFromDate,
|
|
101
|
+
mockTimestampFromMillis,
|
|
102
|
+
mockTimestampNow,
|
|
103
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Mock } from 'node:test';
|
|
2
|
+
import type { Query } from './query.js';
|
|
3
|
+
import type { MockedQuerySnapshot } from './helpers/buildQuerySnapShot.js';
|
|
4
|
+
|
|
5
|
+
export class Transaction {
|
|
6
|
+
getAll(...refsOrReadOptions: Array<Query | Record<string, never>>): Promise<Array<MockedQuerySnapshot>>;
|
|
7
|
+
get(ref: Query): Promise<MockedQuerySnapshot>;
|
|
8
|
+
set(ref: Query): Transaction;
|
|
9
|
+
update(ref: Query): Transaction;
|
|
10
|
+
delete(ref: Query): Transaction;
|
|
11
|
+
create(ref: Query, options: unknown): Transaction;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const mocks: {
|
|
15
|
+
mockGetAll: Mock<any>;
|
|
16
|
+
mockGetAllTransaction: Mock<any>;
|
|
17
|
+
mockGetTransaction: Mock<any>;
|
|
18
|
+
mockSetTransaction: Mock<any>;
|
|
19
|
+
mockUpdateTransaction: Mock<any>;
|
|
20
|
+
mockDeleteTransaction: Mock<any>;
|
|
21
|
+
mockCreateTransaction: Mock<any>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { mock } from 'node:test';
|
|
2
|
+
|
|
3
|
+
export const mockGetAll = mock.fn();
|
|
4
|
+
export const mockGetAllTransaction = mock.fn();
|
|
5
|
+
export const mockGetTransaction = mock.fn();
|
|
6
|
+
export const mockSetTransaction = mock.fn();
|
|
7
|
+
export const mockUpdateTransaction = mock.fn();
|
|
8
|
+
export const mockDeleteTransaction = mock.fn();
|
|
9
|
+
export const mockCreateTransaction = mock.fn();
|
|
10
|
+
|
|
11
|
+
export class Transaction {
|
|
12
|
+
getAll(...refsOrReadOptions) {
|
|
13
|
+
mockGetAll(...arguments);
|
|
14
|
+
mockGetAllTransaction(...arguments);
|
|
15
|
+
// TODO: Assert that read options, if provided, are the last argument
|
|
16
|
+
// Filter out the read options before calling .get()
|
|
17
|
+
return Promise.all(refsOrReadOptions.filter(ref => !!ref.get).map(ref => ref.get()));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get(ref) {
|
|
21
|
+
mockGetTransaction(...arguments);
|
|
22
|
+
return Promise.resolve(ref._get());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
set(ref) {
|
|
26
|
+
mockSetTransaction(...arguments);
|
|
27
|
+
const args = [...arguments];
|
|
28
|
+
args.shift();
|
|
29
|
+
ref.set(...args);
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
update(ref) {
|
|
34
|
+
mockUpdateTransaction(...arguments);
|
|
35
|
+
const args = [...arguments];
|
|
36
|
+
args.shift();
|
|
37
|
+
ref.update(...args);
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
delete(ref) {
|
|
42
|
+
mockDeleteTransaction(...arguments);
|
|
43
|
+
ref.delete();
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
create(ref, options) {
|
|
48
|
+
mockCreateTransaction(...arguments);
|
|
49
|
+
ref.set(options);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const mocks = {
|
|
55
|
+
mockGetAll,
|
|
56
|
+
mockGetAllTransaction,
|
|
57
|
+
mockGetTransaction,
|
|
58
|
+
mockSetTransaction,
|
|
59
|
+
mockUpdateTransaction,
|
|
60
|
+
mockDeleteTransaction,
|
|
61
|
+
mockCreateTransaction,
|
|
62
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "firestore-node-mock",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Helper for mocking Google Cloud Firestore with Node.js test runner",
|
|
5
|
+
"author": "",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"Mock",
|
|
9
|
+
"Firestore"
|
|
10
|
+
],
|
|
11
|
+
"main": "index.js",
|
|
12
|
+
"types": "index.d.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"mocks",
|
|
15
|
+
"index.d.ts",
|
|
16
|
+
"index.d.ts.map",
|
|
17
|
+
"index.js",
|
|
18
|
+
"index.js.map"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"lint": "eslint . --ext .js,.ts",
|
|
23
|
+
"cleanup": "rm -fr ./mocks ./index.d.ts ./index.d.ts.map ./index.js ./index.js.map",
|
|
24
|
+
"prebuild": "npm run cleanup",
|
|
25
|
+
"build": "tsc -P tsconfig.json && cp -r src/mocks .",
|
|
26
|
+
"prepublishOnly": "npm run build",
|
|
27
|
+
"test": "node --experimental-test-module-mocks --test __tests__/**/*.test.js",
|
|
28
|
+
"test:watch": "node --experimental-test-module-mocks --test --watch __tests__/**/*.test.js",
|
|
29
|
+
"test:coverage": "node --experimental-test-module-mocks --test --experimental-test-coverage __tests__/**/*.test.js"
|
|
30
|
+
},
|
|
31
|
+
"lint-staged": {
|
|
32
|
+
"*.{js,css,json,md}": [
|
|
33
|
+
"prettier --write"
|
|
34
|
+
],
|
|
35
|
+
"*.js": [
|
|
36
|
+
"eslint --fix"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/WestonThayer/firestore-node-mock.git"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@babel/core": "^7.5.5",
|
|
45
|
+
"@babel/preset-env": "^7.5.5",
|
|
46
|
+
"@react-native-firebase/firestore": "^15.1.1",
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "^4.17.0",
|
|
48
|
+
"@typescript-eslint/parser": "^4.17.0",
|
|
49
|
+
"babel-eslint": "^10.0.3",
|
|
50
|
+
"eslint": "^7.20.0",
|
|
51
|
+
"eslint-config-prettier": "^8.1.0",
|
|
52
|
+
"firebase": "^8.1.2",
|
|
53
|
+
"firebase-admin": "^9.4.1",
|
|
54
|
+
"husky": "^4.2.1",
|
|
55
|
+
"lint-staged": "^10.0.2",
|
|
56
|
+
"lodash": "^4.17.21",
|
|
57
|
+
"prettier": "^3.1.1",
|
|
58
|
+
"typescript": "^5.0.0",
|
|
59
|
+
"@types/node": "^20.0.0"
|
|
60
|
+
}
|
|
61
|
+
}
|