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 +9 -1
- package/package.json +12 -11
- package/src/ExpoSQLiteNext.ts +86 -64
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
|
-
- **~
|
|
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": "
|
|
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": ">=
|
|
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": "
|
|
38
|
-
"react": "
|
|
39
|
-
"react-test-renderer": "
|
|
40
|
-
"react-native": "~0.
|
|
41
|
-
"@testing-library/react-native": "
|
|
42
|
-
"jest-expo": "~
|
|
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
|
}
|
package/src/ExpoSQLiteNext.ts
CHANGED
|
@@ -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
|
-
|
|
4
|
+
SQLiteBindValue,
|
|
5
|
+
SQLiteOpenOptions,
|
|
7
6
|
SQLiteBindParams,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
),
|
|
21
|
+
NativeDatabase: (
|
|
22
|
+
databaseName: string,
|
|
23
|
+
options?: SQLiteOpenOptions,
|
|
24
|
+
serializedData?: Uint8Array
|
|
25
|
+
) => new NativeDatabase(databaseName, options, serializedData),
|
|
27
26
|
|
|
28
|
-
NativeStatement:
|
|
27
|
+
NativeStatement: () => new NativeStatement(),
|
|
29
28
|
|
|
30
29
|
defaultDatabaseDirectory: '.',
|
|
31
30
|
|
|
32
|
-
ensureDatabasePathExistsAsync:
|
|
33
|
-
ensureDatabasePathExistsSync:
|
|
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(
|
|
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 =
|
|
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) =>
|
|
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 =
|
|
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) =>
|
|
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 =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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 =
|
|
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 =
|
|
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 = (
|
|
111
|
-
|
|
112
|
-
|
|
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 (
|
|
123
|
+
public resetAsync = async (_database: NativeDatabase) => {
|
|
116
124
|
this._reset()
|
|
117
125
|
}
|
|
118
|
-
public finalizeAsync = async (
|
|
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 =
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
138
|
-
|
|
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 =
|
|
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 = (
|
|
150
|
-
public getColumnNamesSync = (
|
|
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 = (
|
|
162
|
+
public resetSync = (_database: NativeDatabase) => {
|
|
155
163
|
this._reset()
|
|
156
164
|
}
|
|
157
|
-
public finalizeSync = (
|
|
165
|
+
public finalizeSync = (_database: NativeDatabase) => {
|
|
158
166
|
this._finalize()
|
|
159
167
|
}
|
|
160
168
|
|
|
161
169
|
//#endregion
|
|
162
170
|
|
|
163
|
-
private _run = (
|
|
164
|
-
|
|
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
|
-
|
|
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
|
}
|