expo-sqlite-mock 2.1.0 → 3.0.0

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 CHANGED
@@ -7,7 +7,8 @@ Use [expo-sqlite](https://docs.expo.dev/versions/latest/sdk/sqlite/) with jest.
7
7
 
8
8
  ## Notice
9
9
 
10
- - **~2.0.0** is for expo-sqlite >=52.
10
+ - **~3.0.0** is for expo-sqlite ~53.
11
+ - **~2.0.0** is for expo-sqlite ~52.
11
12
  - **~1.0.0** is for expo-sqlite ~51.
12
13
 
13
14
  ## Usage
@@ -52,6 +53,13 @@ it("test", async () => {
52
53
 
53
54
  ## Changelog
54
55
 
56
+ - **3.0.0**
57
+ - Compatible with expo-sqlite ~15 and expo ~53.
58
+
59
+ - **2.2.0**
60
+ - Compatible with drizzle
61
+ - Clean up the code
62
+
55
63
  - **2.1.0**
56
64
  - Support custom SQLite database location by setting the `EXPO_SQLITE_MOCK` environment variable.
57
65
  - Update peer dependencies.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-sqlite-mock",
3
- "version": "2.1.0",
3
+ "version": "3.0.0",
4
4
  "license": "MIT",
5
5
  "homepage": "https://github.com/zfben/expo-sqlite-mock",
6
6
  "repository": {
@@ -24,9 +24,9 @@
24
24
  ],
25
25
  "peerDependencies": {
26
26
  "better-sqlite3": "*",
27
- "jest": "*",
27
+ "jest": "~29",
28
28
  "expo-sqlite": "*",
29
- "expo": ">=52"
29
+ "expo": ">=53"
30
30
  },
31
31
  "devDependencies": {
32
32
  "typescript": "*",
@@ -34,13 +34,14 @@
34
34
  "expo-sqlite": "*",
35
35
  "better-sqlite3": "*",
36
36
  "@types/better-sqlite3": "*",
37
- "@types/react": "^18",
38
- "react": "^18",
39
- "react-test-renderer": "^18",
40
- "react-native": "~0.77",
41
- "@testing-library/react-native": "*",
42
- "jest-expo": "~52",
43
- "@types/jest": "*",
44
- "jest": "*"
37
+ "@types/react": "*",
38
+ "react": "*",
39
+ "react-test-renderer": "*",
40
+ "react-native": "~0.80",
41
+ "@testing-library/react-native": "~13",
42
+ "jest-expo": "~53",
43
+ "@types/jest": "~29",
44
+ "jest": "~29",
45
+ "drizzle-orm": "*"
45
46
  }
46
47
  }
@@ -1,15 +1,15 @@
1
- import assert from 'node:assert'
2
1
  import sqlite3 from 'better-sqlite3'
3
2
 
4
- import type { SQLiteOpenOptions } from 'expo-sqlite/src/NativeDatabase'
5
3
  import type {
6
- SQLiteBindBlobParams,
4
+ SQLiteBindValue,
5
+ SQLiteOpenOptions,
7
6
  SQLiteBindParams,
8
- SQLiteBindPrimitiveParams,
9
- SQLiteColumnNames,
10
- SQLiteColumnValues,
11
- SQLiteRunResult,
12
- } from 'expo-sqlite/src/NativeStatement'
7
+ SQLiteRunResult, } from 'expo-sqlite'
8
+
9
+ type SQLiteBindBlobParams = Record<string, Uint8Array>
10
+ type SQLiteBindPrimitiveParams = Record<string, Exclude<SQLiteBindValue, Uint8Array>>
11
+ type SQLiteColumnNames = string[]
12
+ type SQLiteColumnValues = any[]
13
13
 
14
14
  const dbs: NativeDatabase[] = []
15
15
 
@@ -18,19 +18,18 @@ export const mockedExpoSqliteNext = {
18
18
  for (const db of dbs) await db.closeAsync()
19
19
  },
20
20
 
21
- NativeDatabase: jest
22
- .fn()
23
- .mockImplementation(
24
- (databaseName: string, options?: SQLiteOpenOptions, serializedData?: Uint8Array) =>
25
- new NativeDatabase(databaseName, options, serializedData)
26
- ),
21
+ NativeDatabase: (
22
+ databaseName: string,
23
+ options?: SQLiteOpenOptions,
24
+ serializedData?: Uint8Array
25
+ ) => new NativeDatabase(databaseName, options, serializedData),
27
26
 
28
- NativeStatement: jest.fn().mockImplementation(() => new NativeStatement()),
27
+ NativeStatement: () => new NativeStatement(),
29
28
 
30
29
  defaultDatabaseDirectory: '.',
31
30
 
32
- ensureDatabasePathExistsAsync: jest.fn().mockImplementation(async (databasePath: string) => {}),
33
- ensureDatabasePathExistsSync: jest.fn().mockImplementation((databasePath: string) => {}),
31
+ ensureDatabasePathExistsAsync: async (_databasePath: string) => {},
32
+ ensureDatabasePathExistsSync: (_databasePath: string) => {},
34
33
  }
35
34
 
36
35
  //#region async sqlite3
@@ -41,7 +40,11 @@ export const mockedExpoSqliteNext = {
41
40
  class NativeDatabase {
42
41
  private readonly sqlite3Db: sqlite3.Database
43
42
 
44
- constructor(databaseName: string, options?: SQLiteOpenOptions, serializedData?: Uint8Array) {
43
+ constructor(
44
+ _databaseName: string,
45
+ _options?: SQLiteOpenOptions,
46
+ serializedData?: Uint8Array
47
+ ) {
45
48
  if (serializedData != null) {
46
49
  this.sqlite3Db = new sqlite3(Buffer.from(serializedData))
47
50
  } else {
@@ -52,11 +55,12 @@ class NativeDatabase {
52
55
 
53
56
  //#region Asynchronous API
54
57
 
55
- initAsync = jest.fn().mockResolvedValue(null)
58
+ initAsync = () => Promise.resolve()
56
59
  isInTransactionAsync = async () => this.sqlite3Db.inTransaction
57
60
  closeAsync = async () => this.sqlite3Db.close()
58
61
  execAsync = async (source: string) => this.sqlite3Db.exec(source)
59
- serializeAsync = async (databaseName: string) => this.sqlite3Db.serialize({ attached: databaseName })
62
+ serializeAsync = async (databaseName: string) =>
63
+ this.sqlite3Db.serialize({ attached: databaseName })
60
64
  prepareAsync = async (nativeStatement: NativeStatement, source: string) => {
61
65
  nativeStatement.sqlite3Stmt = this.sqlite3Db.prepare(source)
62
66
  }
@@ -65,11 +69,12 @@ class NativeDatabase {
65
69
 
66
70
  //#region Synchronous API
67
71
 
68
- initSync = jest.fn()
72
+ initSync = () => {}
69
73
  isInTransactionSync = () => this.sqlite3Db.inTransaction
70
74
  closeSync = () => this.sqlite3Db.close()
71
75
  execSync = (source: string) => this.sqlite3Db.exec(source)
72
- serializeSync = (databaseName: string) => this.sqlite3Db.serialize({ attached: databaseName })
76
+ serializeSync = (databaseName: string) =>
77
+ this.sqlite3Db.serialize({ attached: databaseName })
73
78
  prepareSync = (nativeStatement: NativeStatement, source: string) => {
74
79
  nativeStatement.sqlite3Stmt = this.sqlite3Db.prepare(source)
75
80
  }
@@ -82,40 +87,43 @@ class NativeDatabase {
82
87
  class NativeStatement {
83
88
  public sqlite3Stmt: sqlite3.Statement | null = null
84
89
  private iterator: ReturnType<sqlite3.Statement['iterate']> | null = null
90
+ private cachedRows: any[] = []
85
91
 
86
92
  //#region Asynchronous API
87
93
 
88
- public runAsync = jest
89
- .fn()
90
- .mockImplementation(
91
- (
92
- database: NativeDatabase,
93
- bindParams: SQLiteBindPrimitiveParams,
94
- bindBlobParams: SQLiteBindBlobParams,
95
- shouldPassAsArray: boolean
96
- ): Promise<SQLiteRunResult & { firstRowValues: SQLiteColumnValues }> =>
97
- Promise.resolve(this._run(normalizeSQLite3Args(bindParams, bindBlobParams, shouldPassAsArray)))
94
+ public runAsync = (
95
+ _database: NativeDatabase,
96
+ bindParams: SQLiteBindPrimitiveParams,
97
+ bindBlobParams: SQLiteBindBlobParams,
98
+ shouldPassAsArray: boolean
99
+ ): Promise<SQLiteRunResult & { firstRowValues: SQLiteColumnValues }> =>
100
+ Promise.resolve(
101
+ this._run(
102
+ normalizeSQLite3Args(bindParams, bindBlobParams, shouldPassAsArray)
103
+ )
98
104
  )
99
- public stepAsync = jest.fn().mockImplementation((database: NativeDatabase): Promise<any> => {
100
- assert(this.sqlite3Stmt)
105
+ public stepAsync = (_database: NativeDatabase): Promise<any> => {
101
106
  if (this.iterator == null) {
102
107
  this.iterator = this.sqlite3Stmt.iterate()
103
108
  // Since the first row is retrieved by `_run()`, we need to skip the first row here.
104
109
  this.iterator.next()
105
110
  }
106
111
  const result = this.iterator.next()
107
- const columnValues = result.done === false ? Object.values(result.value as Record<string, any>) : null
112
+ const columnValues =
113
+ result.done === false
114
+ ? Object.values(result.value as Record<string, any>)
115
+ : null
108
116
  return Promise.resolve(columnValues)
109
- })
110
- public getAllAsync = (database: NativeDatabase) => Promise.resolve(this._allValues())
111
- public getColumnNamesAsync = async (database: NativeDatabase) => {
112
- assert(this.sqlite3Stmt)
117
+ }
118
+ public getAllAsync = (_database: NativeDatabase) =>
119
+ Promise.resolve(this._allValues())
120
+ public getColumnNamesAsync = async (_database: NativeDatabase) => {
113
121
  return this.sqlite3Stmt.columns().map(column => column.name)
114
122
  }
115
- public resetAsync = async (database: NativeDatabase) => {
123
+ public resetAsync = async (_database: NativeDatabase) => {
116
124
  this._reset()
117
125
  }
118
- public finalizeAsync = async (database: NativeDatabase) => {
126
+ public finalizeAsync = async (_database: NativeDatabase) => {
119
127
  this._finalize()
120
128
  }
121
129
 
@@ -123,19 +131,17 @@ class NativeStatement {
123
131
 
124
132
  //#region Synchronous API
125
133
 
126
- public runSync = jest
127
- .fn()
128
- .mockImplementation(
129
- (
130
- database: NativeDatabase,
131
- bindParams: SQLiteBindPrimitiveParams,
132
- bindBlobParams: SQLiteBindBlobParams,
133
- shouldPassAsArray: boolean
134
- ): SQLiteRunResult & { firstRowValues: SQLiteColumnValues } =>
135
- this._run(normalizeSQLite3Args(bindParams, bindBlobParams, shouldPassAsArray))
134
+ public runSync = (
135
+ _database: NativeDatabase,
136
+ bindParams: SQLiteBindPrimitiveParams,
137
+ bindBlobParams: SQLiteBindBlobParams,
138
+ shouldPassAsArray: boolean
139
+ ): SQLiteRunResult & { firstRowValues: SQLiteColumnValues } =>
140
+ this._run(
141
+ normalizeSQLite3Args(bindParams, bindBlobParams, shouldPassAsArray)
136
142
  )
137
- public stepSync = (database: NativeDatabase): any => {
138
- assert(this.sqlite3Stmt)
143
+
144
+ public stepSync = (_database: NativeDatabase): any => {
139
145
  if (this.iterator == null) {
140
146
  this.iterator = this.sqlite3Stmt.iterate()
141
147
  // Since the first row is retrieved by `_run()`, we need to skip the first row here.
@@ -143,33 +149,48 @@ class NativeStatement {
143
149
  }
144
150
 
145
151
  const result = this.iterator.next()
146
- const columnValues = result.done === false ? Object.values(result.value as Record<string, any>) : null
152
+ const columnValues =
153
+ result.done === false
154
+ ? Object.values(result.value as Record<string, any>)
155
+ : null
147
156
  return columnValues
148
157
  }
149
- public getAllSync = (database: NativeDatabase) => this._allValues()
150
- public getColumnNamesSync = (database: NativeDatabase) => {
151
- assert(this.sqlite3Stmt)
158
+ public getAllSync = (_database: NativeDatabase) => this._allValues()
159
+ public getColumnNamesSync = (_database: NativeDatabase) => {
152
160
  return this.sqlite3Stmt.columns().map(column => column.name)
153
161
  }
154
- public resetSync = (database: NativeDatabase) => {
162
+ public resetSync = (_database: NativeDatabase) => {
155
163
  this._reset()
156
164
  }
157
- public finalizeSync = (database: NativeDatabase) => {
165
+ public finalizeSync = (_database: NativeDatabase) => {
158
166
  this._finalize()
159
167
  }
160
168
 
161
169
  //#endregion
162
170
 
163
- private _run = (...params: any[]): SQLiteRunResult & { firstRowValues: SQLiteColumnValues } => {
164
- assert(this.sqlite3Stmt)
171
+ private _run = (
172
+ ...params: any[]
173
+ ): SQLiteRunResult & { firstRowValues: SQLiteColumnValues } => {
165
174
  this.sqlite3Stmt.bind(...params)
175
+
176
+ // Avoid insert with returning being executed multiple times
177
+ if (this.sqlite3Stmt.source.includes(' returning ')) {
178
+ this.cachedRows = this.sqlite3Stmt.all()
179
+
180
+ return {
181
+ lastInsertRowId: this.cachedRows[0].id,
182
+ changes: params.length,
183
+ firstRowValues: Object.values(this.cachedRows[0]),
184
+ }
185
+ }
186
+
166
187
  const result = this.sqlite3Stmt.run()
167
188
 
168
189
  // better-sqlite3 does not support run() returning the first row, use get() instead.
169
190
  let firstRow: any
170
191
  try {
171
192
  firstRow = this.sqlite3Stmt.get()
172
- } catch {
193
+ } catch (_) {
173
194
  // better-sqlite3 may throw `TypeError: This statement does not return data. Use run() instead`
174
195
  firstRow = null
175
196
  }
@@ -181,7 +202,9 @@ class NativeStatement {
181
202
  }
182
203
 
183
204
  private _allValues = (): SQLiteColumnNames[] => {
184
- assert(this.sqlite3Stmt)
205
+ if (this.sqlite3Stmt.source.includes(' returning '))
206
+ return this.cachedRows.slice(1).map(row => Object.values(row))
207
+
185
208
  const sqlite3Stmt = this.sqlite3Stmt as any
186
209
  // Since the first row is retrieved by `_run()`, we need to skip the first row here.
187
210
  return sqlite3Stmt
@@ -191,7 +214,6 @@ class NativeStatement {
191
214
  }
192
215
 
193
216
  private _reset = () => {
194
- assert(this.sqlite3Stmt)
195
217
  this.iterator?.return?.()
196
218
  this.iterator = this.sqlite3Stmt.iterate()
197
219
  }