@sasjs/cli 6.27.1 → 6.27.3
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/build/commands/build/internal/mockSite/assets/sw.js +98 -28
- package/build/commands/build/internal/mockSite/mockSite.js +81 -4
- package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +226 -8
- package/build/commands/build/internal/mockSite/spec/sw.spec.js +378 -112
- package/package.json +3 -3
|
@@ -90,6 +90,7 @@ var path_1 = __importDefault(require("path"));
|
|
|
90
90
|
var api_1 = require("@sasjs/api");
|
|
91
91
|
var os_1 = __importDefault(require("os"));
|
|
92
92
|
var yazl_1 = __importDefault(require("yazl"));
|
|
93
|
+
var utils_1 = require("@sasjs/utils");
|
|
93
94
|
var mockSite_1 = require("../mockSite");
|
|
94
95
|
/**
|
|
95
96
|
* The other half of the parity claim: the service worker is what turns an
|
|
@@ -122,8 +123,9 @@ var lintBundle = function () {
|
|
|
122
123
|
}
|
|
123
124
|
return lintBundlePath;
|
|
124
125
|
};
|
|
125
|
-
var loadWorker = function (base) {
|
|
126
|
+
var loadWorker = function (base, options) {
|
|
126
127
|
if (base === void 0) { base = ''; }
|
|
128
|
+
if (options === void 0) { options = {}; }
|
|
127
129
|
jest.resetModules();
|
|
128
130
|
var handlers = {};
|
|
129
131
|
var refusingWrites = false;
|
|
@@ -205,6 +207,22 @@ var loadWorker = function (base) {
|
|
|
205
207
|
fixture = global.__sasTranscript;
|
|
206
208
|
return [2 /*return*/, fixture ? jsonResponse(fixture) : new Response('', { status: 404 })];
|
|
207
209
|
}
|
|
210
|
+
// the webout() block the build writes beside the worker, from the same
|
|
211
|
+
// @sasjs/utils generator sasjs/server uses. Absent unless a test asks for
|
|
212
|
+
// it: a site built before the block existed keeps the `_webout`-only path.
|
|
213
|
+
if (options.prelude && target.includes('webout-prelude'))
|
|
214
|
+
return [2 /*return*/, jsonResponse({
|
|
215
|
+
sentinels: mockSite_1.WEBOUT_SENTINELS,
|
|
216
|
+
template: (0, utils_1.getWeboutPrelude)({
|
|
217
|
+
weboutPath: mockSite_1.WEBOUT_SENTINELS.weboutPath,
|
|
218
|
+
headersPath: mockSite_1.WEBOUT_SENTINELS.headersPath,
|
|
219
|
+
program: mockSite_1.WEBOUT_SENTINELS.program,
|
|
220
|
+
debug: mockSite_1.WEBOUT_SENTINELS.debug,
|
|
221
|
+
username: mockSite_1.WEBOUT_SENTINELS.username,
|
|
222
|
+
userId: mockSite_1.WEBOUT_SENTINELS.userId,
|
|
223
|
+
apiServerUrl: mockSite_1.WEBOUT_SENTINELS.apiServerUrl
|
|
224
|
+
})
|
|
225
|
+
})];
|
|
208
226
|
return [2 /*return*/, target.includes('vfs-seed')
|
|
209
227
|
? jsonResponse({
|
|
210
228
|
files: (_a = {},
|
|
@@ -260,7 +278,8 @@ var jsonResponse = function (body) { return ({
|
|
|
260
278
|
return [2 /*return*/, body];
|
|
261
279
|
}); }); }
|
|
262
280
|
}); };
|
|
263
|
-
/** A zip archive of text
|
|
281
|
+
/** A zip archive of members - text, or bytes for a base64 member - written the way
|
|
282
|
+
* a deploy upload ships one.
|
|
264
283
|
* The first member is stored rather than deflated, so both methods the reader
|
|
265
284
|
* accepts are exercised by the one case. */
|
|
266
285
|
var zipOf = function (files) {
|
|
@@ -392,6 +411,164 @@ describe('service worker host', function () {
|
|
|
392
411
|
}
|
|
393
412
|
});
|
|
394
413
|
}); });
|
|
414
|
+
it('runs a JS program written the way sasjs/server runs one', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
415
|
+
var worker, response, body, _a, _b;
|
|
416
|
+
var _c;
|
|
417
|
+
return __generator(this, function (_d) {
|
|
418
|
+
switch (_d.label) {
|
|
419
|
+
case 0:
|
|
420
|
+
worker = loadWorker('', { prelude: true });
|
|
421
|
+
worker.runtime.load((_c = {},
|
|
422
|
+
_c[drivePath('services/public/run.js')] = [
|
|
423
|
+
"const rows = [{ name: 'Alice', age: 14 }, { name: 'Bob', age: 15 }]",
|
|
424
|
+
'console.log(`Read ${rows.length} rows`)',
|
|
425
|
+
"webout('OPEN')",
|
|
426
|
+
"webout('OBJ', 'adults', rows.filter((row) => row.age > 13))",
|
|
427
|
+
"webout('CLOSE')"
|
|
428
|
+
].join('\n'),
|
|
429
|
+
_c));
|
|
430
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/stp/execute?_program=' +
|
|
431
|
+
encodeURIComponent("".concat(appLoc, "/services/public/run")))];
|
|
432
|
+
case 1:
|
|
433
|
+
response = _d.sent();
|
|
434
|
+
expect(response.status).toBe(200);
|
|
435
|
+
_b = (_a = JSON).parse;
|
|
436
|
+
return [4 /*yield*/, response.text()];
|
|
437
|
+
case 2:
|
|
438
|
+
body = _b.apply(_a, [_d.sent()]);
|
|
439
|
+
expect(body.adults).toEqual([
|
|
440
|
+
{ name: 'Alice', age: 14 },
|
|
441
|
+
{ name: 'Bob', age: 15 }
|
|
442
|
+
]);
|
|
443
|
+
return [2 /*return*/];
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}); });
|
|
447
|
+
it('answers a Run in Studio on the recorded program, as a code run', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
448
|
+
var worker, response, parts;
|
|
449
|
+
var _a, _b;
|
|
450
|
+
return __generator(this, function (_c) {
|
|
451
|
+
switch (_c.label) {
|
|
452
|
+
case 0:
|
|
453
|
+
// Press Run in Studio posts the buffer to /code/execute with the file's own
|
|
454
|
+
// path prepended as `%let _program` - the path is not a query parameter, as
|
|
455
|
+
// it is for stp/execute. The recording is what answers it: without this the
|
|
456
|
+
// demo's .sas program has a Run button (the host states `sas`) and no way to
|
|
457
|
+
// answer it, because a browser cannot compile SAS.
|
|
458
|
+
;
|
|
459
|
+
global.__sasTranscript = (_a = {},
|
|
460
|
+
_a["".concat(appLoc, "/services/public/program.sas")] = {
|
|
461
|
+
webout: '{"SYSSITE":"00000000"}',
|
|
462
|
+
log: 'NOTE: recorded',
|
|
463
|
+
print: 'The SAS System'
|
|
464
|
+
},
|
|
465
|
+
_a);
|
|
466
|
+
worker = loadWorker();
|
|
467
|
+
worker.runtime.load((_b = {},
|
|
468
|
+
_b[drivePath('services/public/program.sas')] = 'data work.class; run;',
|
|
469
|
+
_b));
|
|
470
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/code/execute', runRequest({
|
|
471
|
+
code: "%let _program = ".concat(appLoc, "/services/public/program.sas;\ndata work.class; run;"),
|
|
472
|
+
runTime: 'sas'
|
|
473
|
+
}))];
|
|
474
|
+
case 1:
|
|
475
|
+
response = _c.sent();
|
|
476
|
+
expect(response.status).toBe(200);
|
|
477
|
+
return [4 /*yield*/, response.text()];
|
|
478
|
+
case 2:
|
|
479
|
+
parts = (_c.sent()).split(LOGS_SEPARATOR);
|
|
480
|
+
expect(parts[0].trim()).toBe('{"SYSSITE":"00000000"}');
|
|
481
|
+
expect(parts[1]).toContain('NOTE: recorded');
|
|
482
|
+
expect(parts[2]).toContain('The SAS System');
|
|
483
|
+
delete global.__sasTranscript;
|
|
484
|
+
return [2 /*return*/];
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
}); });
|
|
488
|
+
it('answers only the line the interface writes, not one further down', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
489
|
+
var worker, response, _a;
|
|
490
|
+
var _b, _c;
|
|
491
|
+
return __generator(this, function (_d) {
|
|
492
|
+
switch (_d.label) {
|
|
493
|
+
case 0:
|
|
494
|
+
// The interface puts `%let _program` first; a line further down belongs to
|
|
495
|
+
// the program. Answered, it would hand a caller's own SAS program the session
|
|
496
|
+
// recorded for another file - the "inventing a session" the refusal exists to
|
|
497
|
+
// avoid, with a different file's log.
|
|
498
|
+
;
|
|
499
|
+
global.__sasTranscript = (_b = {},
|
|
500
|
+
_b["".concat(appLoc, "/services/public/program.sas")] = {
|
|
501
|
+
webout: '{"SYSSITE":"00000000"}',
|
|
502
|
+
log: 'NOTE: recorded',
|
|
503
|
+
print: 'The SAS System'
|
|
504
|
+
},
|
|
505
|
+
_b);
|
|
506
|
+
worker = loadWorker();
|
|
507
|
+
worker.runtime.load((_c = {},
|
|
508
|
+
_c[drivePath('services/public/mine.sas')] = 'data work.class; run;',
|
|
509
|
+
_c));
|
|
510
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/code/execute', runRequest({
|
|
511
|
+
code: "data _null_;\n%let _program = ".concat(appLoc, "/services/public/program.sas;\nrun;"),
|
|
512
|
+
runTime: 'sas'
|
|
513
|
+
}))];
|
|
514
|
+
case 1:
|
|
515
|
+
response = _d.sent();
|
|
516
|
+
expect(response.status).toBe(400);
|
|
517
|
+
_a = expect;
|
|
518
|
+
return [4 /*yield*/, response.text()];
|
|
519
|
+
case 2:
|
|
520
|
+
_a.apply(void 0, [_d.sent()]).toContain('SAS is not available');
|
|
521
|
+
delete global.__sasTranscript;
|
|
522
|
+
return [2 /*return*/];
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
}); });
|
|
526
|
+
it('refuses SAS code that names no recorded program', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
527
|
+
var worker, _a, _b, code, response, body, e_1_1;
|
|
528
|
+
var e_1, _c;
|
|
529
|
+
return __generator(this, function (_d) {
|
|
530
|
+
switch (_d.label) {
|
|
531
|
+
case 0:
|
|
532
|
+
worker = loadWorker();
|
|
533
|
+
_d.label = 1;
|
|
534
|
+
case 1:
|
|
535
|
+
_d.trys.push([1, 7, 8, 9]);
|
|
536
|
+
_a = __values([
|
|
537
|
+
'data work.class; run;',
|
|
538
|
+
"%let _program = ".concat(appLoc, "/services/public/other.sas;\ndata work.class; run;")
|
|
539
|
+
]), _b = _a.next();
|
|
540
|
+
_d.label = 2;
|
|
541
|
+
case 2:
|
|
542
|
+
if (!!_b.done) return [3 /*break*/, 6];
|
|
543
|
+
code = _b.value;
|
|
544
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/code/execute', runRequest({ code: code, runTime: 'sas' }))];
|
|
545
|
+
case 3:
|
|
546
|
+
response = _d.sent();
|
|
547
|
+
expect(response.status).toBe(400);
|
|
548
|
+
return [4 /*yield*/, response.text()];
|
|
549
|
+
case 4:
|
|
550
|
+
body = _d.sent();
|
|
551
|
+
expect(body).toContain('SAS is not available');
|
|
552
|
+
expect(body).not.toContain('SyntaxError');
|
|
553
|
+
_d.label = 5;
|
|
554
|
+
case 5:
|
|
555
|
+
_b = _a.next();
|
|
556
|
+
return [3 /*break*/, 2];
|
|
557
|
+
case 6: return [3 /*break*/, 9];
|
|
558
|
+
case 7:
|
|
559
|
+
e_1_1 = _d.sent();
|
|
560
|
+
e_1 = { error: e_1_1 };
|
|
561
|
+
return [3 /*break*/, 9];
|
|
562
|
+
case 8:
|
|
563
|
+
try {
|
|
564
|
+
if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
|
|
565
|
+
}
|
|
566
|
+
finally { if (e_1) throw e_1.error; }
|
|
567
|
+
return [7 /*endfinally*/];
|
|
568
|
+
case 9: return [2 /*return*/];
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
}); });
|
|
395
572
|
it('refuses a .sas program no recording names, as it always did', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
396
573
|
var worker, response, body;
|
|
397
574
|
var _a;
|
|
@@ -410,7 +587,8 @@ describe('service worker host', function () {
|
|
|
410
587
|
return [4 /*yield*/, response.text()];
|
|
411
588
|
case 2:
|
|
412
589
|
body = _b.sent();
|
|
413
|
-
expect(body).toContain('
|
|
590
|
+
expect(body).toContain('SAS is not available');
|
|
591
|
+
expect(body).toContain('recorded session');
|
|
414
592
|
expect(body).not.toContain('SyntaxError');
|
|
415
593
|
return [2 /*return*/];
|
|
416
594
|
}
|
|
@@ -1546,8 +1724,8 @@ describe('service worker host', function () {
|
|
|
1546
1724
|
});
|
|
1547
1725
|
}); });
|
|
1548
1726
|
it('publishes, lists, serves and removes an app through the package surface', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1549
|
-
var dispatch, _a, _b, path_2, listing, _c,
|
|
1550
|
-
var
|
|
1727
|
+
var dispatch, _a, _b, path_2, listing, _c, e_2_1, _d, _e, path_3, landing, e_3_1, root, archive, form, published, app, _f, _g, path_4, listing, _h, e_4_1, _j, _k, path_5, page, _l, e_5_1, folder, _m, refused, missing, removed, _o, after, _p, gone;
|
|
1728
|
+
var e_2, _q, e_3, _r, e_4, _s, e_5, _t;
|
|
1551
1729
|
return __generator(this, function (_u) {
|
|
1552
1730
|
switch (_u.label) {
|
|
1553
1731
|
case 0:
|
|
@@ -1574,14 +1752,14 @@ describe('service worker host', function () {
|
|
|
1574
1752
|
return [3 /*break*/, 2];
|
|
1575
1753
|
case 6: return [3 /*break*/, 9];
|
|
1576
1754
|
case 7:
|
|
1577
|
-
|
|
1578
|
-
|
|
1755
|
+
e_2_1 = _u.sent();
|
|
1756
|
+
e_2 = { error: e_2_1 };
|
|
1579
1757
|
return [3 /*break*/, 9];
|
|
1580
1758
|
case 8:
|
|
1581
1759
|
try {
|
|
1582
1760
|
if (_b && !_b.done && (_q = _a.return)) _q.call(_a);
|
|
1583
1761
|
}
|
|
1584
|
-
finally { if (
|
|
1762
|
+
finally { if (e_2) throw e_2.error; }
|
|
1585
1763
|
return [7 /*endfinally*/];
|
|
1586
1764
|
case 9:
|
|
1587
1765
|
_u.trys.push([9, 14, 15, 16]);
|
|
@@ -1601,14 +1779,14 @@ describe('service worker host', function () {
|
|
|
1601
1779
|
return [3 /*break*/, 10];
|
|
1602
1780
|
case 13: return [3 /*break*/, 16];
|
|
1603
1781
|
case 14:
|
|
1604
|
-
|
|
1605
|
-
|
|
1782
|
+
e_3_1 = _u.sent();
|
|
1783
|
+
e_3 = { error: e_3_1 };
|
|
1606
1784
|
return [3 /*break*/, 16];
|
|
1607
1785
|
case 15:
|
|
1608
1786
|
try {
|
|
1609
1787
|
if (_e && !_e.done && (_r = _d.return)) _r.call(_d);
|
|
1610
1788
|
}
|
|
1611
|
-
finally { if (
|
|
1789
|
+
finally { if (e_3) throw e_3.error; }
|
|
1612
1790
|
return [7 /*endfinally*/];
|
|
1613
1791
|
case 16: return [4 /*yield*/, dispatch('/AppStream')];
|
|
1614
1792
|
case 17:
|
|
@@ -1661,14 +1839,14 @@ describe('service worker host', function () {
|
|
|
1661
1839
|
return [3 /*break*/, 22];
|
|
1662
1840
|
case 26: return [3 /*break*/, 29];
|
|
1663
1841
|
case 27:
|
|
1664
|
-
|
|
1665
|
-
|
|
1842
|
+
e_4_1 = _u.sent();
|
|
1843
|
+
e_4 = { error: e_4_1 };
|
|
1666
1844
|
return [3 /*break*/, 29];
|
|
1667
1845
|
case 28:
|
|
1668
1846
|
try {
|
|
1669
1847
|
if (_g && !_g.done && (_s = _f.return)) _s.call(_f);
|
|
1670
1848
|
}
|
|
1671
|
-
finally { if (
|
|
1849
|
+
finally { if (e_4) throw e_4.error; }
|
|
1672
1850
|
return [7 /*endfinally*/];
|
|
1673
1851
|
case 29:
|
|
1674
1852
|
_u.trys.push([29, 35, 36, 37]);
|
|
@@ -1695,14 +1873,14 @@ describe('service worker host', function () {
|
|
|
1695
1873
|
return [3 /*break*/, 30];
|
|
1696
1874
|
case 34: return [3 /*break*/, 37];
|
|
1697
1875
|
case 35:
|
|
1698
|
-
|
|
1699
|
-
|
|
1876
|
+
e_5_1 = _u.sent();
|
|
1877
|
+
e_5 = { error: e_5_1 };
|
|
1700
1878
|
return [3 /*break*/, 37];
|
|
1701
1879
|
case 36:
|
|
1702
1880
|
try {
|
|
1703
1881
|
if (_k && !_k.done && (_t = _j.return)) _t.call(_j);
|
|
1704
1882
|
}
|
|
1705
|
-
finally { if (
|
|
1883
|
+
finally { if (e_5) throw e_5.error; }
|
|
1706
1884
|
return [7 /*endfinally*/];
|
|
1707
1885
|
case 37: return [4 /*yield*/, dispatch("/app/".concat(app.id))];
|
|
1708
1886
|
case 38:
|
|
@@ -2005,30 +2183,33 @@ describe('service worker host', function () {
|
|
|
2005
2183
|
case 0:
|
|
2006
2184
|
dispatch = seeded().dispatch;
|
|
2007
2185
|
return [4 /*yield*/, dispatch('/SASjsApi/info')
|
|
2008
|
-
// the documented body, field for field
|
|
2009
|
-
//
|
|
2010
|
-
//
|
|
2011
|
-
//
|
|
2186
|
+
// the documented body, field for field: a property the document does not
|
|
2187
|
+
// state, or a login form told there are accounts when there are none, is
|
|
2188
|
+
// this host describing an API the server does not serve. `runTimes` is the
|
|
2189
|
+
// host's own, not the package's browser fixture: `sas` is what makes the
|
|
2190
|
+
// Studio offer Run on a .sas file, and the recording is how it is answered.
|
|
2012
2191
|
];
|
|
2013
2192
|
case 1:
|
|
2014
2193
|
info = _f.sent();
|
|
2015
|
-
// the documented body, field for field
|
|
2016
|
-
//
|
|
2017
|
-
//
|
|
2018
|
-
//
|
|
2194
|
+
// the documented body, field for field: a property the document does not
|
|
2195
|
+
// state, or a login form told there are accounts when there are none, is
|
|
2196
|
+
// this host describing an API the server does not serve. `runTimes` is the
|
|
2197
|
+
// host's own, not the package's browser fixture: `sas` is what makes the
|
|
2198
|
+
// Studio offer Run on a .sas file, and the recording is how it is answered.
|
|
2019
2199
|
_a = expect;
|
|
2020
2200
|
return [4 /*yield*/, info.json()];
|
|
2021
2201
|
case 2:
|
|
2022
|
-
// the documented body, field for field
|
|
2023
|
-
//
|
|
2024
|
-
//
|
|
2025
|
-
//
|
|
2202
|
+
// the documented body, field for field: a property the document does not
|
|
2203
|
+
// state, or a login form told there are accounts when there are none, is
|
|
2204
|
+
// this host describing an API the server does not serve. `runTimes` is the
|
|
2205
|
+
// host's own, not the package's browser fixture: `sas` is what makes the
|
|
2206
|
+
// Studio offer Run on a .sas file, and the recording is how it is answered.
|
|
2026
2207
|
_a.apply(void 0, [_f.sent()]).toEqual({
|
|
2027
2208
|
mode: 'desktop',
|
|
2028
2209
|
cors: 'disable',
|
|
2029
2210
|
httpPort: null,
|
|
2030
2211
|
httpsPort: null,
|
|
2031
|
-
runTimes: ['js'],
|
|
2212
|
+
runTimes: ['js', 'sas'],
|
|
2032
2213
|
authProviders: [],
|
|
2033
2214
|
localLoginEnabled: false,
|
|
2034
2215
|
version: 'dev'
|
|
@@ -2641,7 +2822,7 @@ describe('service worker host', function () {
|
|
|
2641
2822
|
}); });
|
|
2642
2823
|
});
|
|
2643
2824
|
describe('conformance corpus', function () {
|
|
2644
|
-
var
|
|
2825
|
+
var e_6, _a, e_7, _b;
|
|
2645
2826
|
/**
|
|
2646
2827
|
* The corpus is what "the same API" means: the cases @sasjs/api runs against
|
|
2647
2828
|
* its own services, and against a live sasjs/server, run here against this
|
|
@@ -2650,7 +2831,7 @@ describe('service worker host', function () {
|
|
|
2650
2831
|
* used to have - and it is a test rather than a review.
|
|
2651
2832
|
*/
|
|
2652
2833
|
var httpFor = function (testCase) {
|
|
2653
|
-
var
|
|
2834
|
+
var e_8, _a;
|
|
2654
2835
|
var _b;
|
|
2655
2836
|
var _c = __read(testCase.route.split(' '), 2), method = _c[0], routePath = _c[1];
|
|
2656
2837
|
var query = new URLSearchParams();
|
|
@@ -2661,12 +2842,12 @@ describe('service worker host', function () {
|
|
|
2661
2842
|
query.set(key, String(value));
|
|
2662
2843
|
}
|
|
2663
2844
|
}
|
|
2664
|
-
catch (
|
|
2845
|
+
catch (e_8_1) { e_8 = { error: e_8_1 }; }
|
|
2665
2846
|
finally {
|
|
2666
2847
|
try {
|
|
2667
2848
|
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
|
|
2668
2849
|
}
|
|
2669
|
-
finally { if (
|
|
2850
|
+
finally { if (e_8) throw e_8.error; }
|
|
2670
2851
|
}
|
|
2671
2852
|
var suffix = query.toString() ? "?".concat(query) : '';
|
|
2672
2853
|
var init = { method: method };
|
|
@@ -2697,8 +2878,8 @@ describe('service worker host', function () {
|
|
|
2697
2878
|
* does.
|
|
2698
2879
|
*/
|
|
2699
2880
|
var seedDrive = function (runtime, testCase) { return __awaiter(void 0, void 0, void 0, function () {
|
|
2700
|
-
var text, _a, _b, _c, file, contents, _d, _e, _f, file, contents,
|
|
2701
|
-
var
|
|
2881
|
+
var text, _a, _b, _c, file, contents, _d, _e, _f, file, contents, e_9_1;
|
|
2882
|
+
var e_10, _g, e_9, _h;
|
|
2702
2883
|
return __generator(this, function (_j) {
|
|
2703
2884
|
switch (_j.label) {
|
|
2704
2885
|
case 0:
|
|
@@ -2710,12 +2891,12 @@ describe('service worker host', function () {
|
|
|
2710
2891
|
text["/sasjs/drive/files".concat(file)] = contents;
|
|
2711
2892
|
}
|
|
2712
2893
|
}
|
|
2713
|
-
catch (
|
|
2894
|
+
catch (e_10_1) { e_10 = { error: e_10_1 }; }
|
|
2714
2895
|
finally {
|
|
2715
2896
|
try {
|
|
2716
2897
|
if (_b && !_b.done && (_g = _a.return)) _g.call(_a);
|
|
2717
2898
|
}
|
|
2718
|
-
finally { if (
|
|
2899
|
+
finally { if (e_10) throw e_10.error; }
|
|
2719
2900
|
}
|
|
2720
2901
|
return [4 /*yield*/, runtime.load(text)];
|
|
2721
2902
|
case 1:
|
|
@@ -2738,14 +2919,14 @@ describe('service worker host', function () {
|
|
|
2738
2919
|
return [3 /*break*/, 3];
|
|
2739
2920
|
case 6: return [3 /*break*/, 9];
|
|
2740
2921
|
case 7:
|
|
2741
|
-
|
|
2742
|
-
|
|
2922
|
+
e_9_1 = _j.sent();
|
|
2923
|
+
e_9 = { error: e_9_1 };
|
|
2743
2924
|
return [3 /*break*/, 9];
|
|
2744
2925
|
case 8:
|
|
2745
2926
|
try {
|
|
2746
2927
|
if (_e && !_e.done && (_h = _d.return)) _h.call(_d);
|
|
2747
2928
|
}
|
|
2748
|
-
finally { if (
|
|
2929
|
+
finally { if (e_9) throw e_9.error; }
|
|
2749
2930
|
return [7 /*endfinally*/];
|
|
2750
2931
|
case 9: return [2 /*return*/];
|
|
2751
2932
|
}
|
|
@@ -2856,29 +3037,13 @@ describe('service worker host', function () {
|
|
|
2856
3037
|
_loop_1(testCase);
|
|
2857
3038
|
}
|
|
2858
3039
|
}
|
|
2859
|
-
catch (
|
|
3040
|
+
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
|
2860
3041
|
finally {
|
|
2861
3042
|
try {
|
|
2862
3043
|
if (DRIVE_CORPUS_1_1 && !DRIVE_CORPUS_1_1.done && (_a = DRIVE_CORPUS_1.return)) _a.call(DRIVE_CORPUS_1);
|
|
2863
3044
|
}
|
|
2864
|
-
finally { if (
|
|
3045
|
+
finally { if (e_6) throw e_6.error; }
|
|
2865
3046
|
}
|
|
2866
|
-
/**
|
|
2867
|
-
* The deploy route's JSON form and its removal, which is what `sasjs deploy`
|
|
2868
|
-
* sends and what this host's transport carries unchanged.
|
|
2869
|
-
*
|
|
2870
|
-
* The upload route's cases hand over the expanded tree in place of the
|
|
2871
|
-
* archive, which is a host's transport detail rather than an answer: this
|
|
2872
|
-
* worker takes the archive and expands it, so those cases would pin the
|
|
2873
|
-
* corpus's assumption instead of this host's behaviour. The zip cases above
|
|
2874
|
-
* pin that route. The removal cases need an app to remove, so the index is
|
|
2875
|
-
* seeded with the corpus's own - the worker starts with nothing published,
|
|
2876
|
-
* which is the server's state too.
|
|
2877
|
-
*
|
|
2878
|
-
* The state a case leaves is checked too - `expectDrive` - because both deploy
|
|
2879
|
-
* answers are `200` and the difference between replacing a folder and keeping
|
|
2880
|
-
* what is there is only visible in the drive afterwards.
|
|
2881
|
-
*/
|
|
2882
3047
|
/** The app a removal case names, as the corpus states it. */
|
|
2883
3048
|
var CORPUS_APP = {
|
|
2884
3049
|
appLoc: '/Public/myApp',
|
|
@@ -2932,10 +3097,92 @@ describe('service worker host', function () {
|
|
|
2932
3097
|
}
|
|
2933
3098
|
});
|
|
2934
3099
|
}); };
|
|
3100
|
+
/**
|
|
3101
|
+
* The archive a client uploads, built from the tree the case states.
|
|
3102
|
+
*
|
|
3103
|
+
* The corpus is transport-agnostic: an upload case gives the tree the archive
|
|
3104
|
+
* has to expand to, and the folder in the query where the document puts it.
|
|
3105
|
+
* This host takes an archive, so the harness makes one - which is the only way
|
|
3106
|
+
* the mock's own expansion, and the tree it produces, gets exercised by the
|
|
3107
|
+
* cases that describe it.
|
|
3108
|
+
*/
|
|
3109
|
+
var archiveOf = function (tree) { return __awaiter(void 0, void 0, void 0, function () {
|
|
3110
|
+
var files, walk;
|
|
3111
|
+
return __generator(this, function (_a) {
|
|
3112
|
+
files = {};
|
|
3113
|
+
walk = function (members, prefix) {
|
|
3114
|
+
var e_11, _a;
|
|
3115
|
+
var _b;
|
|
3116
|
+
try {
|
|
3117
|
+
for (var _c = __values(members || []), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
3118
|
+
var member = _d.value;
|
|
3119
|
+
var at = prefix ? "".concat(prefix, "/").concat(member.name) : member.name;
|
|
3120
|
+
if (member.type === 'folder')
|
|
3121
|
+
walk(member.members, at);
|
|
3122
|
+
else
|
|
3123
|
+
files[at] =
|
|
3124
|
+
member.encoding === 'base64'
|
|
3125
|
+
? new Uint8Array(Buffer.from(String(member.code), 'base64'))
|
|
3126
|
+
: String((_b = member.code) !== null && _b !== void 0 ? _b : '');
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
catch (e_11_1) { e_11 = { error: e_11_1 }; }
|
|
3130
|
+
finally {
|
|
3131
|
+
try {
|
|
3132
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
3133
|
+
}
|
|
3134
|
+
finally { if (e_11) throw e_11.error; }
|
|
3135
|
+
}
|
|
3136
|
+
};
|
|
3137
|
+
walk(tree === null || tree === void 0 ? void 0 : tree.members, '');
|
|
3138
|
+
return [2 /*return*/, zipOf(files)];
|
|
3139
|
+
});
|
|
3140
|
+
}); };
|
|
3141
|
+
/**
|
|
3142
|
+
* The request a deploy case describes, as HTTP.
|
|
3143
|
+
*
|
|
3144
|
+
* The JSON and removal routes carry their fields as they are stated. The upload
|
|
3145
|
+
* route's body is an archive, so the tree becomes one and the fields the case
|
|
3146
|
+
* states beside it travel as form fields - a host's multipart body, with the
|
|
3147
|
+
* folder also in the query where the document puts it.
|
|
3148
|
+
*/
|
|
3149
|
+
var deployRequest = function (testCase) { return __awaiter(void 0, void 0, void 0, function () {
|
|
3150
|
+
var _a, requestPath, init, body, archive, form, _b, _c, _d, field, value;
|
|
3151
|
+
var e_12, _e;
|
|
3152
|
+
return __generator(this, function (_f) {
|
|
3153
|
+
switch (_f.label) {
|
|
3154
|
+
case 0:
|
|
3155
|
+
_a = httpFor(testCase), requestPath = _a.path, init = _a.init;
|
|
3156
|
+
if (!testCase.route.endsWith('/deploy/upload'))
|
|
3157
|
+
return [2 /*return*/, { requestPath: requestPath, init: init }];
|
|
3158
|
+
body = (testCase.request.body || {});
|
|
3159
|
+
return [4 /*yield*/, archiveOf(body.fileTree)];
|
|
3160
|
+
case 1:
|
|
3161
|
+
archive = _f.sent();
|
|
3162
|
+
form = new FormData();
|
|
3163
|
+
try {
|
|
3164
|
+
for (_b = __values(Object.entries(body)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
3165
|
+
_d = __read(_c.value, 2), field = _d[0], value = _d[1];
|
|
3166
|
+
if (field !== 'fileTree' && value !== undefined)
|
|
3167
|
+
form.set(field, String(value));
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
catch (e_12_1) { e_12 = { error: e_12_1 }; }
|
|
3171
|
+
finally {
|
|
3172
|
+
try {
|
|
3173
|
+
if (_c && !_c.done && (_e = _b.return)) _e.call(_b);
|
|
3174
|
+
}
|
|
3175
|
+
finally { if (e_12) throw e_12.error; }
|
|
3176
|
+
}
|
|
3177
|
+
form.set('file', new Blob([new Uint8Array(archive)]), 'deploy.zip');
|
|
3178
|
+
return [2 /*return*/, { requestPath: requestPath, init: { method: 'POST', body: form } }];
|
|
3179
|
+
}
|
|
3180
|
+
});
|
|
3181
|
+
}); };
|
|
2935
3182
|
var _loop_2 = function (testCase) {
|
|
2936
3183
|
it(testCase.id, function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2937
|
-
var worker, _a, requestPath, init, response, listing, ids, _b, _c, path_6, file,
|
|
2938
|
-
var
|
|
3184
|
+
var worker, _a, requestPath, init, response, listing, ids, _b, _c, path_6, file, e_13_1, _d, _e, path_7, file, e_14_1;
|
|
3185
|
+
var e_13, _f, e_14, _g;
|
|
2939
3186
|
var _h, _j, _k;
|
|
2940
3187
|
return __generator(this, function (_l) {
|
|
2941
3188
|
switch (_l.label) {
|
|
@@ -2944,102 +3191,121 @@ describe('service worker host', function () {
|
|
|
2944
3191
|
return [4 /*yield*/, seedDrive(worker.runtime, testCase)];
|
|
2945
3192
|
case 1:
|
|
2946
3193
|
_l.sent();
|
|
2947
|
-
return [4 /*yield*/, seedIndex(worker.runtime, testCase)
|
|
2948
|
-
// `unwritable` is not passed on here: the case that states it is the
|
|
2949
|
-
// upload route's, which the filter above keeps out, and the deploy route's
|
|
2950
|
-
// own `500` is covered by `deploy-refused-by-store` in the drive loop.
|
|
2951
|
-
];
|
|
3194
|
+
return [4 /*yield*/, seedIndex(worker.runtime, testCase)];
|
|
2952
3195
|
case 2:
|
|
2953
3196
|
_l.sent();
|
|
2954
|
-
|
|
2955
|
-
return [4 /*yield*/,
|
|
3197
|
+
worker.refuseWrites(Boolean(testCase.unwritable));
|
|
3198
|
+
return [4 /*yield*/, deployRequest(testCase)];
|
|
2956
3199
|
case 3:
|
|
3200
|
+
_a = _l.sent(), requestPath = _a.requestPath, init = _a.init;
|
|
3201
|
+
return [4 /*yield*/, worker.dispatch(requestPath, init)];
|
|
3202
|
+
case 4:
|
|
2957
3203
|
response = _l.sent();
|
|
2958
3204
|
return [4 /*yield*/, assertExpectations(response, testCase)
|
|
2959
3205
|
// a removal takes the app the id names, and leaves the rest published
|
|
2960
3206
|
];
|
|
2961
|
-
case
|
|
3207
|
+
case 5:
|
|
2962
3208
|
_l.sent();
|
|
2963
|
-
if (!(testCase.route === 'DELETE /SASjsApi/drive/deploy')) return [3 /*break*/,
|
|
3209
|
+
if (!(testCase.route === 'DELETE /SASjsApi/drive/deploy')) return [3 /*break*/, 8];
|
|
2964
3210
|
return [4 /*yield*/, worker.dispatch('/app/apps.json')];
|
|
2965
|
-
case
|
|
3211
|
+
case 6:
|
|
2966
3212
|
listing = _l.sent();
|
|
2967
3213
|
return [4 /*yield*/, listing.json()];
|
|
2968
|
-
case
|
|
3214
|
+
case 7:
|
|
2969
3215
|
ids = (_l.sent()).apps.map(function (app) { return app.id; });
|
|
2970
3216
|
expect(ids).toContain(BYSTANDER.id);
|
|
2971
3217
|
if (response.status === 200)
|
|
2972
3218
|
expect(ids).not.toContain((_h = testCase.request.body) === null || _h === void 0 ? void 0 : _h.id);
|
|
2973
|
-
_l.label = 7;
|
|
2974
|
-
case 7:
|
|
2975
|
-
_l.trys.push([7, 12, 13, 14]);
|
|
2976
|
-
_b = __values(((_j = testCase.expectDrive) === null || _j === void 0 ? void 0 : _j.present) || []), _c = _b.next();
|
|
2977
3219
|
_l.label = 8;
|
|
2978
3220
|
case 8:
|
|
2979
|
-
|
|
3221
|
+
_l.trys.push([8, 13, 14, 15]);
|
|
3222
|
+
_b = __values(((_j = testCase.expectDrive) === null || _j === void 0 ? void 0 : _j.present) || []), _c = _b.next();
|
|
3223
|
+
_l.label = 9;
|
|
3224
|
+
case 9:
|
|
3225
|
+
if (!!_c.done) return [3 /*break*/, 12];
|
|
2980
3226
|
path_6 = _c.value;
|
|
2981
3227
|
return [4 /*yield*/, worker.dispatch('/SASjsApi/drive/file?_filePath=' + encodeURIComponent(path_6))];
|
|
2982
|
-
case
|
|
3228
|
+
case 10:
|
|
2983
3229
|
file = _l.sent();
|
|
2984
3230
|
expect({ path: path_6, status: file.status }).toEqual({ path: path_6, status: 200 });
|
|
2985
|
-
_l.label =
|
|
2986
|
-
case
|
|
3231
|
+
_l.label = 11;
|
|
3232
|
+
case 11:
|
|
2987
3233
|
_c = _b.next();
|
|
2988
|
-
return [3 /*break*/,
|
|
2989
|
-
case
|
|
2990
|
-
case 12:
|
|
2991
|
-
e_10_1 = _l.sent();
|
|
2992
|
-
e_10 = { error: e_10_1 };
|
|
2993
|
-
return [3 /*break*/, 14];
|
|
3234
|
+
return [3 /*break*/, 9];
|
|
3235
|
+
case 12: return [3 /*break*/, 15];
|
|
2994
3236
|
case 13:
|
|
3237
|
+
e_13_1 = _l.sent();
|
|
3238
|
+
e_13 = { error: e_13_1 };
|
|
3239
|
+
return [3 /*break*/, 15];
|
|
3240
|
+
case 14:
|
|
2995
3241
|
try {
|
|
2996
3242
|
if (_c && !_c.done && (_f = _b.return)) _f.call(_b);
|
|
2997
3243
|
}
|
|
2998
|
-
finally { if (
|
|
3244
|
+
finally { if (e_13) throw e_13.error; }
|
|
2999
3245
|
return [7 /*endfinally*/];
|
|
3000
|
-
case 14:
|
|
3001
|
-
_l.trys.push([14, 19, 20, 21]);
|
|
3002
|
-
_d = __values(((_k = testCase.expectDrive) === null || _k === void 0 ? void 0 : _k.missing) || []), _e = _d.next();
|
|
3003
|
-
_l.label = 15;
|
|
3004
3246
|
case 15:
|
|
3005
|
-
|
|
3247
|
+
_l.trys.push([15, 20, 21, 22]);
|
|
3248
|
+
_d = __values(((_k = testCase.expectDrive) === null || _k === void 0 ? void 0 : _k.missing) || []), _e = _d.next();
|
|
3249
|
+
_l.label = 16;
|
|
3250
|
+
case 16:
|
|
3251
|
+
if (!!_e.done) return [3 /*break*/, 19];
|
|
3006
3252
|
path_7 = _e.value;
|
|
3007
3253
|
return [4 /*yield*/, worker.dispatch('/SASjsApi/drive/file?_filePath=' + encodeURIComponent(path_7))];
|
|
3008
|
-
case
|
|
3254
|
+
case 17:
|
|
3009
3255
|
file = _l.sent();
|
|
3010
3256
|
expect({ path: path_7, status: file.status }).toEqual({ path: path_7, status: 404 });
|
|
3011
|
-
_l.label =
|
|
3012
|
-
case
|
|
3257
|
+
_l.label = 18;
|
|
3258
|
+
case 18:
|
|
3013
3259
|
_e = _d.next();
|
|
3014
|
-
return [3 /*break*/,
|
|
3015
|
-
case
|
|
3016
|
-
case 19:
|
|
3017
|
-
e_11_1 = _l.sent();
|
|
3018
|
-
e_11 = { error: e_11_1 };
|
|
3019
|
-
return [3 /*break*/, 21];
|
|
3260
|
+
return [3 /*break*/, 16];
|
|
3261
|
+
case 19: return [3 /*break*/, 22];
|
|
3020
3262
|
case 20:
|
|
3263
|
+
e_14_1 = _l.sent();
|
|
3264
|
+
e_14 = { error: e_14_1 };
|
|
3265
|
+
return [3 /*break*/, 22];
|
|
3266
|
+
case 21:
|
|
3021
3267
|
try {
|
|
3022
3268
|
if (_e && !_e.done && (_g = _d.return)) _g.call(_d);
|
|
3023
3269
|
}
|
|
3024
|
-
finally { if (
|
|
3270
|
+
finally { if (e_14) throw e_14.error; }
|
|
3025
3271
|
return [7 /*endfinally*/];
|
|
3026
|
-
case
|
|
3272
|
+
case 22: return [2 /*return*/];
|
|
3027
3273
|
}
|
|
3028
3274
|
});
|
|
3029
3275
|
}); });
|
|
3030
3276
|
};
|
|
3031
3277
|
try {
|
|
3032
|
-
|
|
3278
|
+
/**
|
|
3279
|
+
* The deploy routes: the JSON form and the removal, plus the upload route
|
|
3280
|
+
* through an archive this harness builds from the tree the case states.
|
|
3281
|
+
*
|
|
3282
|
+
* The corpus describes an upload by what the archive has to expand to, so a
|
|
3283
|
+
* host that takes bytes can run the same case as one that is handed an
|
|
3284
|
+
* expansion - `deployRequest` makes the archive, and what it asserts is this
|
|
3285
|
+
* worker's own unzipping and tree-building rather than the corpus's
|
|
3286
|
+
* assumption. The one case that cannot travel this way is the expansion that is
|
|
3287
|
+
* not a tree, which the corpus marks `transport: 'expansion'`.
|
|
3288
|
+
*
|
|
3289
|
+
* The removal cases need an app to remove, so the index is seeded with the one
|
|
3290
|
+
* the case names and a bystander that must survive it. The state a case leaves
|
|
3291
|
+
* is checked too - `expectDrive` - because both deploy answers are `200` and
|
|
3292
|
+
* replacing a folder only differs from keeping it in what the drive holds
|
|
3293
|
+
* afterwards.
|
|
3294
|
+
*/
|
|
3295
|
+
for (var _c = __values(api_1.DEPLOY_CORPUS.filter(
|
|
3296
|
+
// a case a host that takes an archive cannot produce, which the corpus marks
|
|
3297
|
+
// on the case itself rather than leaving each host to name it
|
|
3298
|
+
function (entry) { return entry.transport !== 'expansion'; })), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
3033
3299
|
var testCase = _d.value;
|
|
3034
3300
|
_loop_2(testCase);
|
|
3035
3301
|
}
|
|
3036
3302
|
}
|
|
3037
|
-
catch (
|
|
3303
|
+
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
|
3038
3304
|
finally {
|
|
3039
3305
|
try {
|
|
3040
3306
|
if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
|
|
3041
3307
|
}
|
|
3042
|
-
finally { if (
|
|
3308
|
+
finally { if (e_7) throw e_7.error; }
|
|
3043
3309
|
}
|
|
3044
3310
|
});
|
|
3045
3311
|
/**
|
|
@@ -3051,7 +3317,7 @@ describe('service worker host', function () {
|
|
|
3051
3317
|
* are the package's.
|
|
3052
3318
|
*/
|
|
3053
3319
|
describe('identity and record conformance', function () {
|
|
3054
|
-
var
|
|
3320
|
+
var e_15, _a;
|
|
3055
3321
|
/**
|
|
3056
3322
|
* Boots the worker from a fixture, the way the page does: the case's own
|
|
3057
3323
|
* identity, and `subject: null` for no implicit caller, so a case that
|
|
@@ -3084,7 +3350,7 @@ describe('service worker host', function () {
|
|
|
3084
3350
|
};
|
|
3085
3351
|
/** The request a case describes, as HTTP: path parameters filled, token sent. */
|
|
3086
3352
|
var httpForCase = function (testCase) {
|
|
3087
|
-
var
|
|
3353
|
+
var e_16, _a;
|
|
3088
3354
|
var _b = __read(testCase.route.split(' '), 2), method = _b[0], routePath = _b[1];
|
|
3089
3355
|
var filled = routePath.replace(/\{(\w+)\}/g, function (_match, name) {
|
|
3090
3356
|
return encodeURIComponent(String((testCase.request.params || {})[name]));
|
|
@@ -3097,12 +3363,12 @@ describe('service worker host', function () {
|
|
|
3097
3363
|
query.set(key, String(value));
|
|
3098
3364
|
}
|
|
3099
3365
|
}
|
|
3100
|
-
catch (
|
|
3366
|
+
catch (e_16_1) { e_16 = { error: e_16_1 }; }
|
|
3101
3367
|
finally {
|
|
3102
3368
|
try {
|
|
3103
3369
|
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
3104
3370
|
}
|
|
3105
|
-
finally { if (
|
|
3371
|
+
finally { if (e_16) throw e_16.error; }
|
|
3106
3372
|
}
|
|
3107
3373
|
var suffix = query.toString() ? "?".concat(query) : '';
|
|
3108
3374
|
var headers = {};
|
|
@@ -3224,12 +3490,12 @@ describe('service worker host', function () {
|
|
|
3224
3490
|
_loop_3(testCase);
|
|
3225
3491
|
}
|
|
3226
3492
|
}
|
|
3227
|
-
catch (
|
|
3493
|
+
catch (e_15_1) { e_15 = { error: e_15_1 }; }
|
|
3228
3494
|
finally {
|
|
3229
3495
|
try {
|
|
3230
3496
|
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
3231
3497
|
}
|
|
3232
|
-
finally { if (
|
|
3498
|
+
finally { if (e_15) throw e_15.error; }
|
|
3233
3499
|
}
|
|
3234
3500
|
it('answers as the fixture administrator when no subject was chosen', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
3235
3501
|
var worker, response, rules;
|