meadow 2.0.23 → 2.0.27
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 +110 -141
- package/docs/README.md +34 -230
- package/docs/_cover.md +14 -0
- package/docs/_sidebar.md +44 -12
- package/docs/_topbar.md +5 -0
- package/docs/api/doCount.md +109 -0
- package/docs/api/doCreate.md +132 -0
- package/docs/api/doDelete.md +101 -0
- package/docs/api/doRead.md +122 -0
- package/docs/api/doReads.md +136 -0
- package/docs/api/doUndelete.md +98 -0
- package/docs/api/doUpdate.md +129 -0
- package/docs/api/getRoleName.md +84 -0
- package/docs/api/loadFromPackage.md +153 -0
- package/docs/api/marshalRecordFromSourceToObject.md +92 -0
- package/docs/api/query.md +133 -0
- package/docs/api/rawQueries.md +197 -0
- package/docs/api/reference.md +117 -0
- package/docs/api/setAuthorizer.md +103 -0
- package/docs/api/setDefault.md +90 -0
- package/docs/api/setDefaultIdentifier.md +84 -0
- package/docs/api/setDomain.md +56 -0
- package/docs/api/setIDUser.md +91 -0
- package/docs/api/setJsonSchema.md +92 -0
- package/docs/api/setProvider.md +87 -0
- package/docs/api/setSchema.md +107 -0
- package/docs/api/setScope.md +68 -0
- package/docs/api/validateObject.md +119 -0
- package/docs/architecture.md +316 -0
- package/docs/audit-tracking.md +226 -0
- package/docs/configuration.md +317 -0
- package/docs/providers/meadow-endpoints.md +306 -0
- package/docs/providers/mongodb.md +319 -0
- package/docs/providers/postgresql.md +312 -0
- package/docs/providers/rocksdb.md +297 -0
- package/docs/query-dsl.md +269 -0
- package/docs/quick-start.md +384 -0
- package/docs/raw-queries.md +193 -0
- package/docs/retold-catalog.json +61 -1
- package/docs/retold-keyword-index.json +15860 -4839
- package/docs/soft-deletes.md +224 -0
- package/package.json +44 -13
- package/scripts/bookstore-seed-postgresql.sql +135 -0
- package/scripts/dgraph-test-db.sh +144 -0
- package/scripts/meadow-test-cleanup.sh +5 -1
- package/scripts/mongodb-test-db.sh +98 -0
- package/scripts/postgresql-test-db.sh +124 -0
- package/scripts/solr-test-db.sh +135 -0
- package/source/Meadow.js +5 -0
- package/source/providers/Meadow-Provider-DGraph.js +679 -0
- package/source/providers/Meadow-Provider-MeadowEndpoints.js +1 -1
- package/source/providers/Meadow-Provider-MongoDB.js +527 -0
- package/source/providers/Meadow-Provider-PostgreSQL.js +361 -0
- package/source/providers/Meadow-Provider-RocksDB.js +1300 -0
- package/source/providers/Meadow-Provider-Solr.js +726 -0
- package/test/Meadow-Provider-DGraph_tests.js +741 -0
- package/test/Meadow-Provider-MongoDB_tests.js +661 -0
- package/test/Meadow-Provider-PostgreSQL_tests.js +787 -0
- package/test/Meadow-Provider-RocksDB_tests.js +887 -0
- package/test/Meadow-Provider-SQLiteBrowser-Headless_tests.js +657 -0
- package/test/Meadow-Provider-SQLiteBrowser_tests.js +895 -0
- package/test/Meadow-Provider-Solr_tests.js +679 -0
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless browser integration tests for the Meadow SQLite provider
|
|
3
|
+
* using meadow-connection-sqlite-browser (sql.js / WASM).
|
|
4
|
+
*
|
|
5
|
+
* Verifies the full Meadow ORM CRUD pipeline works in a real browser:
|
|
6
|
+
* Fable → MeadowConnectionSQLiteBrowser → Meadow DAL → doCreate/doRead/doUpdate/doDelete
|
|
7
|
+
*
|
|
8
|
+
* Loads four bundles in headless Chrome:
|
|
9
|
+
* 1) sql-wasm.js (global initSqlJs)
|
|
10
|
+
* 2) fable.min.js from CDN (global Fable)
|
|
11
|
+
* 3) meadow.js from dist/ (global Meadow)
|
|
12
|
+
* 4) meadow-connection-sqlite-browser.js from connection package dist/ (global MeadowConnectionSqliteBrowser)
|
|
13
|
+
*
|
|
14
|
+
* Requires:
|
|
15
|
+
* - npm run build (so meadow dist/ exists)
|
|
16
|
+
* - meadow-connection-sqlite-browser built (its dist/ must exist)
|
|
17
|
+
* - puppeteer installed
|
|
18
|
+
*
|
|
19
|
+
* @license MIT
|
|
20
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
var Chai = require('chai');
|
|
24
|
+
var Expect = Chai.expect;
|
|
25
|
+
|
|
26
|
+
var libHTTP = require('http');
|
|
27
|
+
var libFS = require('fs');
|
|
28
|
+
var libPath = require('path');
|
|
29
|
+
|
|
30
|
+
var _MeadowRoot = libPath.resolve(__dirname, '..');
|
|
31
|
+
var _MeadowDistDir = libPath.join(_MeadowRoot, 'dist');
|
|
32
|
+
|
|
33
|
+
// Resolve connection package paths
|
|
34
|
+
var _ConnectionPackageRoot = libPath.resolve(_MeadowRoot, '..', 'meadow-connection-sqlite-browser');
|
|
35
|
+
var _ConnectionDistDir = libPath.join(_ConnectionPackageRoot, 'dist');
|
|
36
|
+
var _SqlJsDistDir = libPath.join(_ConnectionPackageRoot, 'node_modules', 'sql.js', 'dist');
|
|
37
|
+
|
|
38
|
+
// Fable loaded from CDN
|
|
39
|
+
var _FABLE_CDN_URL = 'https://cdn.jsdelivr.net/npm/fable@3/dist/fable.min.js';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create a simple HTTP server that serves the static files needed
|
|
43
|
+
* for the browser test page.
|
|
44
|
+
*
|
|
45
|
+
* @param {function} fCallback - Callback with (pError, pServer, pPort)
|
|
46
|
+
*/
|
|
47
|
+
function startTestServer(fCallback)
|
|
48
|
+
{
|
|
49
|
+
var tmpMimeTypes =
|
|
50
|
+
{
|
|
51
|
+
'.html': 'text/html',
|
|
52
|
+
'.js': 'application/javascript',
|
|
53
|
+
'.wasm': 'application/wasm',
|
|
54
|
+
'.map': 'application/json'
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
var tmpServer = libHTTP.createServer(
|
|
58
|
+
function(pRequest, pResponse)
|
|
59
|
+
{
|
|
60
|
+
var tmpUrl = pRequest.url;
|
|
61
|
+
|
|
62
|
+
// Route: / → test page
|
|
63
|
+
if (tmpUrl === '/' || tmpUrl === '/index.html')
|
|
64
|
+
{
|
|
65
|
+
pResponse.writeHead(200, { 'Content-Type': 'text/html' });
|
|
66
|
+
pResponse.end(generateTestHTML());
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Route: /sql-wasm.js, /sql-wasm.wasm → from sql.js dist
|
|
71
|
+
if (tmpUrl === '/sql-wasm.js' || tmpUrl === '/sql-wasm.wasm')
|
|
72
|
+
{
|
|
73
|
+
serveFile(libPath.join(_SqlJsDistDir, tmpUrl.slice(1)), pResponse, tmpMimeTypes);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Route: /meadow.js → from meadow dist/
|
|
78
|
+
if (tmpUrl === '/meadow.js')
|
|
79
|
+
{
|
|
80
|
+
serveFile(libPath.join(_MeadowDistDir, 'meadow.js'), pResponse, tmpMimeTypes);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Route: /meadow-connection-sqlite-browser.js → from connection dist/
|
|
85
|
+
if (tmpUrl.startsWith('/meadow-connection-sqlite-browser'))
|
|
86
|
+
{
|
|
87
|
+
serveFile(libPath.join(_ConnectionDistDir, tmpUrl.slice(1)), pResponse, tmpMimeTypes);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pResponse.writeHead(404);
|
|
92
|
+
pResponse.end('Not Found');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
tmpServer.listen(0, '127.0.0.1',
|
|
96
|
+
function()
|
|
97
|
+
{
|
|
98
|
+
var tmpPort = tmpServer.address().port;
|
|
99
|
+
return fCallback(null, tmpServer, tmpPort);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Serve a static file.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} pFilePath - Absolute path
|
|
107
|
+
* @param {object} pResponse - HTTP response
|
|
108
|
+
* @param {object} pMimeTypes - Extension → MIME type map
|
|
109
|
+
*/
|
|
110
|
+
function serveFile(pFilePath, pResponse, pMimeTypes)
|
|
111
|
+
{
|
|
112
|
+
if (!libFS.existsSync(pFilePath))
|
|
113
|
+
{
|
|
114
|
+
pResponse.writeHead(404);
|
|
115
|
+
pResponse.end('File not found: ' + pFilePath);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
var tmpExt = libPath.extname(pFilePath);
|
|
120
|
+
var tmpContentType = pMimeTypes[tmpExt] || 'application/octet-stream';
|
|
121
|
+
|
|
122
|
+
var tmpContent = libFS.readFileSync(pFilePath);
|
|
123
|
+
pResponse.writeHead(200, { 'Content-Type': tmpContentType });
|
|
124
|
+
pResponse.end(tmpContent);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Generate the test HTML page.
|
|
129
|
+
*
|
|
130
|
+
* Exercises the full Meadow CRUD pipeline in the browser:
|
|
131
|
+
* Fable → register MeadowSQLiteProvider → connectAsync → create table →
|
|
132
|
+
* seed data → Meadow.new() → setProvider('SQLite') → doCreate/doRead/
|
|
133
|
+
* doReads/doUpdate/doDelete/doCount
|
|
134
|
+
*
|
|
135
|
+
* @returns {string} HTML content
|
|
136
|
+
*/
|
|
137
|
+
function generateTestHTML()
|
|
138
|
+
{
|
|
139
|
+
return `<!DOCTYPE html>
|
|
140
|
+
<html>
|
|
141
|
+
<head><title>Meadow SQLiteBrowser Headless CRUD Tests</title></head>
|
|
142
|
+
<body>
|
|
143
|
+
<h1>Meadow CRUD — Headless Browser</h1>
|
|
144
|
+
<pre id="output">Running tests...</pre>
|
|
145
|
+
|
|
146
|
+
<!-- 1) sql.js WASM loader -->
|
|
147
|
+
<script src="/sql-wasm.js"></script>
|
|
148
|
+
|
|
149
|
+
<!-- 2) Fable from CDN -->
|
|
150
|
+
<script src="${_FABLE_CDN_URL}"></script>
|
|
151
|
+
|
|
152
|
+
<!-- 3) Meadow ORM (includes foxhound + providers) -->
|
|
153
|
+
<script src="/meadow.js"></script>
|
|
154
|
+
|
|
155
|
+
<!-- 4) Browser SQLite connection provider -->
|
|
156
|
+
<script src="/meadow-connection-sqlite-browser.js"></script>
|
|
157
|
+
|
|
158
|
+
<script>
|
|
159
|
+
(async function runTests()
|
|
160
|
+
{
|
|
161
|
+
var results = [];
|
|
162
|
+
var output = document.getElementById('output');
|
|
163
|
+
|
|
164
|
+
function addResult(pName, pPassed, pError)
|
|
165
|
+
{
|
|
166
|
+
results.push({ name: pName, passed: pPassed, error: pError || null });
|
|
167
|
+
output.textContent += '\\n' + (pPassed ? 'PASS' : 'FAIL') + ': ' + pName;
|
|
168
|
+
if (pError) { output.textContent += ' (' + pError + ')'; }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Wrap Meadow callback-style CRUD in promises for sequential await
|
|
172
|
+
function promisify(pFn)
|
|
173
|
+
{
|
|
174
|
+
return function()
|
|
175
|
+
{
|
|
176
|
+
var args = Array.prototype.slice.call(arguments);
|
|
177
|
+
return new Promise(function(resolve, reject)
|
|
178
|
+
{
|
|
179
|
+
args.push(function()
|
|
180
|
+
{
|
|
181
|
+
// Meadow callbacks vary: (err, query, record) or (err, query, queryRead, record)
|
|
182
|
+
var cbArgs = Array.prototype.slice.call(arguments);
|
|
183
|
+
var err = cbArgs[0];
|
|
184
|
+
if (err) { return reject(err); }
|
|
185
|
+
resolve(cbArgs);
|
|
186
|
+
});
|
|
187
|
+
pFn.apply(null, args);
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try
|
|
193
|
+
{
|
|
194
|
+
// ---- Test 1: Globals available ----
|
|
195
|
+
addResult('globals available',
|
|
196
|
+
typeof Fable === 'function'
|
|
197
|
+
&& typeof Meadow === 'object'
|
|
198
|
+
&& typeof Meadow.new === 'function'
|
|
199
|
+
&& typeof MeadowConnectionSqliteBrowser === 'function'
|
|
200
|
+
&& typeof initSqlJs === 'function'
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// ---- Test 2: Create Fable and register connection ----
|
|
204
|
+
var fable = new Fable({
|
|
205
|
+
Product: 'MeadowBrowserCRUD',
|
|
206
|
+
ProductVersion: '1.0.0',
|
|
207
|
+
LogStreams: [{ streamtype: 'console' }]
|
|
208
|
+
});
|
|
209
|
+
fable.serviceManager.addServiceType('MeadowSQLiteProvider', MeadowConnectionSqliteBrowser);
|
|
210
|
+
var sqliteConn = fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
|
|
211
|
+
addResult('fable + connection registered',
|
|
212
|
+
fable.isFable === true
|
|
213
|
+
&& typeof fable.MeadowSQLiteProvider === 'object'
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// ---- Test 3: connectAsync ----
|
|
217
|
+
await new Promise(function(resolve, reject)
|
|
218
|
+
{
|
|
219
|
+
sqliteConn.connectAsync(function(pError)
|
|
220
|
+
{
|
|
221
|
+
if (pError) { return reject(pError); }
|
|
222
|
+
addResult('connectAsync succeeded', sqliteConn.connected === true);
|
|
223
|
+
resolve();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// ---- Test 4: Create table and seed data ----
|
|
228
|
+
var db = sqliteConn.db;
|
|
229
|
+
db.exec(
|
|
230
|
+
"CREATE TABLE IF NOT EXISTS FableTest (" +
|
|
231
|
+
" IDAnimal INTEGER PRIMARY KEY AUTOINCREMENT," +
|
|
232
|
+
" GUIDAnimal TEXT NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'," +
|
|
233
|
+
" CreateDate TEXT," +
|
|
234
|
+
" CreatingIDUser INTEGER NOT NULL DEFAULT 0," +
|
|
235
|
+
" UpdateDate TEXT," +
|
|
236
|
+
" UpdatingIDUser INTEGER NOT NULL DEFAULT 0," +
|
|
237
|
+
" Deleted INTEGER NOT NULL DEFAULT 0," +
|
|
238
|
+
" DeleteDate TEXT," +
|
|
239
|
+
" DeletingIDUser INTEGER NOT NULL DEFAULT 0," +
|
|
240
|
+
" Name TEXT NOT NULL DEFAULT ''," +
|
|
241
|
+
" Type TEXT NOT NULL DEFAULT ''" +
|
|
242
|
+
");"
|
|
243
|
+
);
|
|
244
|
+
var ins = db.prepare(
|
|
245
|
+
"INSERT INTO FableTest (GUIDAnimal, CreateDate, CreatingIDUser, UpdateDate, UpdatingIDUser, Deleted, Name, Type) " +
|
|
246
|
+
"VALUES ('00000000-0000-0000-0000-000000000000', datetime('now'), 1, datetime('now'), 1, 0, ?, ?)"
|
|
247
|
+
);
|
|
248
|
+
ins.run('Foo Foo', 'Bunny');
|
|
249
|
+
ins.run('Red Riding Hood', 'Girl');
|
|
250
|
+
ins.run('Red', 'Dog');
|
|
251
|
+
ins.run('Spot', 'Dog');
|
|
252
|
+
ins.run('Gertrude', 'Frog');
|
|
253
|
+
|
|
254
|
+
var seedCheck = db.prepare('SELECT COUNT(*) AS cnt FROM FableTest').get();
|
|
255
|
+
addResult('table created and seeded', seedCheck.cnt === 5);
|
|
256
|
+
|
|
257
|
+
// ---- Set up Meadow DAL ----
|
|
258
|
+
var animalSchema = [
|
|
259
|
+
{ Column: 'IDAnimal', Type: 'AutoIdentity' },
|
|
260
|
+
{ Column: 'GUIDAnimal', Type: 'AutoGUID' },
|
|
261
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
262
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
263
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
264
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
265
|
+
{ Column: 'Deleted', Type: 'Deleted' },
|
|
266
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
267
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' }
|
|
268
|
+
];
|
|
269
|
+
var animalJsonSchema = {
|
|
270
|
+
title: 'Animal',
|
|
271
|
+
description: 'A creature that lives in a meadow.',
|
|
272
|
+
type: 'object',
|
|
273
|
+
properties: {
|
|
274
|
+
IDAnimal: { description: 'The unique identifier for an animal', type: 'integer' },
|
|
275
|
+
Name: { description: "The animal's name", type: 'string' },
|
|
276
|
+
Type: { description: 'The type of the animal', type: 'string' }
|
|
277
|
+
},
|
|
278
|
+
required: ['IDAnimal', 'Name', 'CreatingIDUser']
|
|
279
|
+
};
|
|
280
|
+
var animalDefault = {
|
|
281
|
+
IDAnimal: null, GUIDAnimal: '',
|
|
282
|
+
CreateDate: false, CreatingIDUser: 0,
|
|
283
|
+
UpdateDate: false, UpdatingIDUser: 0,
|
|
284
|
+
Deleted: 0, DeleteDate: false, DeletingIDUser: 0,
|
|
285
|
+
Name: 'Unknown', Type: 'Unclassified'
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
function newMeadow()
|
|
289
|
+
{
|
|
290
|
+
return Meadow.new(fable, 'FableTest')
|
|
291
|
+
.setProvider('SQLite')
|
|
292
|
+
.setSchema(animalSchema)
|
|
293
|
+
.setJsonSchema(animalJsonSchema)
|
|
294
|
+
.setDefaultIdentifier('IDAnimal')
|
|
295
|
+
.setDefault(animalDefault);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ---- Test 5: doCreate ----
|
|
299
|
+
var dal = newMeadow().setIDUser(90210);
|
|
300
|
+
var createResult = await new Promise(function(resolve, reject)
|
|
301
|
+
{
|
|
302
|
+
var q = dal.query.clone().addRecord({ Name: 'Blastoise', Type: 'Pokemon' });
|
|
303
|
+
dal.doCreate(q, function(pError, pQuery, pQueryRead, pRecord)
|
|
304
|
+
{
|
|
305
|
+
if (pError) { return reject(pError); }
|
|
306
|
+
resolve(pRecord);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
addResult('doCreate',
|
|
310
|
+
createResult.Name === 'Blastoise'
|
|
311
|
+
&& createResult.CreatingIDUser === 90210
|
|
312
|
+
&& typeof createResult.IDAnimal === 'number'
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
// ---- Test 6: doRead ----
|
|
316
|
+
dal = newMeadow();
|
|
317
|
+
var readResult = await new Promise(function(resolve, reject)
|
|
318
|
+
{
|
|
319
|
+
var q = dal.query.addFilter('IDAnimal', 1);
|
|
320
|
+
dal.doRead(q, function(pError, pQuery, pRecord)
|
|
321
|
+
{
|
|
322
|
+
if (pError) { return reject(pError); }
|
|
323
|
+
resolve(pRecord);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
addResult('doRead',
|
|
327
|
+
readResult.IDAnimal === 1
|
|
328
|
+
&& readResult.Name === 'Foo Foo'
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
// ---- Test 7: doReads ----
|
|
332
|
+
dal = newMeadow();
|
|
333
|
+
var readsResult = await new Promise(function(resolve, reject)
|
|
334
|
+
{
|
|
335
|
+
dal.doReads(dal.query, function(pError, pQuery, pRecords)
|
|
336
|
+
{
|
|
337
|
+
if (pError) { return reject(pError); }
|
|
338
|
+
resolve(pRecords);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
addResult('doReads',
|
|
342
|
+
Array.isArray(readsResult)
|
|
343
|
+
&& readsResult.length >= 5
|
|
344
|
+
&& readsResult[0].Name === 'Foo Foo'
|
|
345
|
+
&& readsResult[1].Name === 'Red Riding Hood'
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
// ---- Test 8: doUpdate ----
|
|
349
|
+
dal = newMeadow();
|
|
350
|
+
var updateResult = await new Promise(function(resolve, reject)
|
|
351
|
+
{
|
|
352
|
+
var q = dal.query.addRecord({ IDAnimal: 2, Type: 'Human' });
|
|
353
|
+
dal.doUpdate(q, function(pError, pQuery, pQueryRead, pRecord)
|
|
354
|
+
{
|
|
355
|
+
if (pError) { return reject(pError); }
|
|
356
|
+
resolve(pRecord);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
addResult('doUpdate',
|
|
360
|
+
updateResult.Type === 'Human'
|
|
361
|
+
&& updateResult.IDAnimal === 2
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
// ---- Test 9: doDelete ----
|
|
365
|
+
dal = newMeadow();
|
|
366
|
+
var deleteResult = await new Promise(function(resolve, reject)
|
|
367
|
+
{
|
|
368
|
+
var q = dal.query.addFilter('IDAnimal', 3);
|
|
369
|
+
dal.doDelete(q, function(pError, pQuery, pRecord)
|
|
370
|
+
{
|
|
371
|
+
if (pError) { return reject(pError); }
|
|
372
|
+
resolve(pRecord);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
addResult('doDelete', deleteResult === 1);
|
|
376
|
+
|
|
377
|
+
// ---- Test 10: doUndelete ----
|
|
378
|
+
dal = newMeadow();
|
|
379
|
+
// First delete #5
|
|
380
|
+
await new Promise(function(resolve, reject)
|
|
381
|
+
{
|
|
382
|
+
var q = dal.query.addFilter('IDAnimal', 5);
|
|
383
|
+
dal.doDelete(q, function(pError) { pError ? reject(pError) : resolve(); });
|
|
384
|
+
});
|
|
385
|
+
dal = newMeadow();
|
|
386
|
+
var undeleteResult = await new Promise(function(resolve, reject)
|
|
387
|
+
{
|
|
388
|
+
var q = dal.query.addFilter('IDAnimal', 5);
|
|
389
|
+
dal.doUndelete(q, function(pError, pQuery, pRecord)
|
|
390
|
+
{
|
|
391
|
+
if (pError) { return reject(pError); }
|
|
392
|
+
resolve(pRecord);
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
addResult('doUndelete', undeleteResult === 1);
|
|
396
|
+
|
|
397
|
+
// ---- Test 11: doCount ----
|
|
398
|
+
dal = newMeadow();
|
|
399
|
+
var countResult = await new Promise(function(resolve, reject)
|
|
400
|
+
{
|
|
401
|
+
dal.doCount(dal.query, function(pError, pQuery, pRecord)
|
|
402
|
+
{
|
|
403
|
+
if (pError) { return reject(pError); }
|
|
404
|
+
resolve(pRecord);
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
addResult('doCount',
|
|
408
|
+
typeof countResult === 'number'
|
|
409
|
+
&& countResult >= 4
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
// ---- Test 12: doCreate with predefined GUID ----
|
|
413
|
+
dal = newMeadow();
|
|
414
|
+
var guidResult = await new Promise(function(resolve, reject)
|
|
415
|
+
{
|
|
416
|
+
var q = dal.query.clone().addRecord({
|
|
417
|
+
Name: 'MewThree', GUIDAnimal: '0xBROWSER123', Type: 'Pokemon'
|
|
418
|
+
});
|
|
419
|
+
dal.doCreate(q, function(pError, pQuery, pQueryRead, pRecord)
|
|
420
|
+
{
|
|
421
|
+
if (pError) { return reject(pError); }
|
|
422
|
+
resolve(pRecord);
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
addResult('doCreate with predefined GUID',
|
|
426
|
+
guidResult.Name === 'MewThree'
|
|
427
|
+
&& guidResult.GUIDAnimal === '0xBROWSER123'
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
// ---- Test 13: doRead by GUID ----
|
|
431
|
+
dal = newMeadow();
|
|
432
|
+
var guidReadResult = await new Promise(function(resolve, reject)
|
|
433
|
+
{
|
|
434
|
+
var q = dal.query.addFilter('GUIDAnimal', '0xBROWSER123');
|
|
435
|
+
dal.doRead(q, function(pError, pQuery, pRecord)
|
|
436
|
+
{
|
|
437
|
+
if (pError) { return reject(pError); }
|
|
438
|
+
resolve(pRecord);
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
addResult('doRead by GUID',
|
|
442
|
+
guidReadResult.Name === 'MewThree'
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// ---- Test 14: doRead with no match returns false ----
|
|
446
|
+
dal = newMeadow();
|
|
447
|
+
var noMatchResult = await new Promise(function(resolve, reject)
|
|
448
|
+
{
|
|
449
|
+
var q = dal.query.addFilter('IDAnimal', 99999);
|
|
450
|
+
dal.doRead(q, function(pError, pQuery, pRecord)
|
|
451
|
+
{
|
|
452
|
+
if (pError) { return reject(pError); }
|
|
453
|
+
resolve(pRecord);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
addResult('doRead no match returns false', noMatchResult === false);
|
|
457
|
+
}
|
|
458
|
+
catch (pError)
|
|
459
|
+
{
|
|
460
|
+
addResult('unexpected error', false, pError.message || String(pError));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
window.__TEST_RESULTS__ = results;
|
|
464
|
+
window.__TESTS_COMPLETE__ = true;
|
|
465
|
+
output.textContent += '\\n\\nDone: '
|
|
466
|
+
+ results.filter(function(r) { return r.passed; }).length + '/'
|
|
467
|
+
+ results.length + ' passed';
|
|
468
|
+
})();
|
|
469
|
+
</script>
|
|
470
|
+
</body>
|
|
471
|
+
</html>`;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
suite
|
|
475
|
+
(
|
|
476
|
+
'Meadow-Provider-SQLiteBrowser-Headless',
|
|
477
|
+
function()
|
|
478
|
+
{
|
|
479
|
+
this.timeout(60000);
|
|
480
|
+
|
|
481
|
+
var _Server;
|
|
482
|
+
var _Port;
|
|
483
|
+
var _Browser;
|
|
484
|
+
var _Puppeteer;
|
|
485
|
+
|
|
486
|
+
suiteSetup
|
|
487
|
+
(
|
|
488
|
+
function(fDone)
|
|
489
|
+
{
|
|
490
|
+
// Verify meadow dist exists
|
|
491
|
+
if (!libFS.existsSync(libPath.join(_MeadowDistDir, 'meadow.js')))
|
|
492
|
+
{
|
|
493
|
+
return fDone(new Error(
|
|
494
|
+
'dist/meadow.js not found. Run "npm run build" in the meadow package first.'
|
|
495
|
+
));
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Verify connection package dist exists
|
|
499
|
+
if (!libFS.existsSync(libPath.join(_ConnectionDistDir, 'meadow-connection-sqlite-browser.js')))
|
|
500
|
+
{
|
|
501
|
+
return fDone(new Error(
|
|
502
|
+
'meadow-connection-sqlite-browser dist not found. Run "npm run build" in that package first.'
|
|
503
|
+
));
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Verify sql.js dist exists
|
|
507
|
+
if (!libFS.existsSync(libPath.join(_SqlJsDistDir, 'sql-wasm.js')))
|
|
508
|
+
{
|
|
509
|
+
return fDone(new Error(
|
|
510
|
+
'sql.js dist files not found. Run "npm install" in meadow-connection-sqlite-browser first.'
|
|
511
|
+
));
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Start the test server
|
|
515
|
+
startTestServer(
|
|
516
|
+
function(pError, pServer, pPort)
|
|
517
|
+
{
|
|
518
|
+
if (pError)
|
|
519
|
+
{
|
|
520
|
+
return fDone(pError);
|
|
521
|
+
}
|
|
522
|
+
_Server = pServer;
|
|
523
|
+
_Port = pPort;
|
|
524
|
+
|
|
525
|
+
try
|
|
526
|
+
{
|
|
527
|
+
_Puppeteer = require('puppeteer');
|
|
528
|
+
}
|
|
529
|
+
catch (pRequireError)
|
|
530
|
+
{
|
|
531
|
+
_Server.close();
|
|
532
|
+
return fDone(new Error(
|
|
533
|
+
'puppeteer is not installed. Add it as a devDependency and run npm install.'
|
|
534
|
+
));
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return fDone();
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
suiteTeardown
|
|
543
|
+
(
|
|
544
|
+
function(fDone)
|
|
545
|
+
{
|
|
546
|
+
var tmpSteps = [];
|
|
547
|
+
|
|
548
|
+
if (_Browser)
|
|
549
|
+
{
|
|
550
|
+
tmpSteps.push(_Browser.close().catch(function() {}));
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
Promise.all(tmpSteps).then(
|
|
554
|
+
function()
|
|
555
|
+
{
|
|
556
|
+
if (_Server)
|
|
557
|
+
{
|
|
558
|
+
_Server.close(fDone);
|
|
559
|
+
}
|
|
560
|
+
else
|
|
561
|
+
{
|
|
562
|
+
fDone();
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
test
|
|
569
|
+
(
|
|
570
|
+
'Full Meadow CRUD pipeline works in headless Chrome',
|
|
571
|
+
function(fDone)
|
|
572
|
+
{
|
|
573
|
+
_Puppeteer.launch(
|
|
574
|
+
{
|
|
575
|
+
headless: true,
|
|
576
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
|
577
|
+
})
|
|
578
|
+
.then(
|
|
579
|
+
function(pBrowser)
|
|
580
|
+
{
|
|
581
|
+
_Browser = pBrowser;
|
|
582
|
+
return _Browser.newPage();
|
|
583
|
+
})
|
|
584
|
+
.then(
|
|
585
|
+
function(pPage)
|
|
586
|
+
{
|
|
587
|
+
pPage.on('console',
|
|
588
|
+
function(pMessage)
|
|
589
|
+
{
|
|
590
|
+
if (pMessage.type() === 'error')
|
|
591
|
+
{
|
|
592
|
+
console.log(' [browser error]', pMessage.text());
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
pPage.on('pageerror',
|
|
597
|
+
function(pError)
|
|
598
|
+
{
|
|
599
|
+
console.log(' [browser page error]', pError.message);
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
return pPage.goto('http://127.0.0.1:' + _Port + '/', { waitUntil: 'networkidle0', timeout: 30000 })
|
|
603
|
+
.then(function() { return pPage; });
|
|
604
|
+
})
|
|
605
|
+
.then(
|
|
606
|
+
function(pPage)
|
|
607
|
+
{
|
|
608
|
+
return pPage.waitForFunction(
|
|
609
|
+
'window.__TESTS_COMPLETE__ === true',
|
|
610
|
+
{ timeout: 30000 }
|
|
611
|
+
).then(function() { return pPage; });
|
|
612
|
+
})
|
|
613
|
+
.then(
|
|
614
|
+
function(pPage)
|
|
615
|
+
{
|
|
616
|
+
return pPage.evaluate(function()
|
|
617
|
+
{
|
|
618
|
+
return window.__TEST_RESULTS__;
|
|
619
|
+
});
|
|
620
|
+
})
|
|
621
|
+
.then(
|
|
622
|
+
function(pResults)
|
|
623
|
+
{
|
|
624
|
+
Expect(pResults).to.be.an('array');
|
|
625
|
+
Expect(pResults.length).to.be.above(0);
|
|
626
|
+
|
|
627
|
+
var tmpFailures = [];
|
|
628
|
+
|
|
629
|
+
for (var i = 0; i < pResults.length; i++)
|
|
630
|
+
{
|
|
631
|
+
if (!pResults[i].passed)
|
|
632
|
+
{
|
|
633
|
+
tmpFailures.push(
|
|
634
|
+
pResults[i].name + (pResults[i].error ? ': ' + pResults[i].error : '')
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
if (tmpFailures.length > 0)
|
|
640
|
+
{
|
|
641
|
+
Expect.fail(
|
|
642
|
+
'Browser CRUD tests failed:\n - ' + tmpFailures.join('\n - ')
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
console.log(' ' + pResults.length + ' browser CRUD sub-tests passed');
|
|
647
|
+
fDone();
|
|
648
|
+
})
|
|
649
|
+
.catch(
|
|
650
|
+
function(pError)
|
|
651
|
+
{
|
|
652
|
+
fDone(pError);
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
);
|