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.
@@ -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
+ }