@sasjs/cli 6.14.0 → 6.15.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/build/commands/build/internal/mockSite/assets/sw.js +509 -174
- package/build/commands/build/internal/mockSite/mockSite.js +32 -8
- package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +78 -5
- package/build/commands/build/internal/mockSite/spec/sw.spec.js +532 -56
- package/package.json +1 -1
|
@@ -35,6 +35,17 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
35
35
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
+
var __values = (this && this.__values) || function(o) {
|
|
39
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
40
|
+
if (m) return m.call(o);
|
|
41
|
+
if (o && typeof o.length === "number") return {
|
|
42
|
+
next: function () {
|
|
43
|
+
if (o && i >= o.length) o = void 0;
|
|
44
|
+
return { value: o && o[i++], done: !o };
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
48
|
+
};
|
|
38
49
|
var __read = (this && this.__read) || function (o, n) {
|
|
39
50
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
40
51
|
if (!m) return o;
|
|
@@ -51,23 +62,13 @@ var __read = (this && this.__read) || function (o, n) {
|
|
|
51
62
|
}
|
|
52
63
|
return ar;
|
|
53
64
|
};
|
|
54
|
-
var __values = (this && this.__values) || function(o) {
|
|
55
|
-
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
56
|
-
if (m) return m.call(o);
|
|
57
|
-
if (o && typeof o.length === "number") return {
|
|
58
|
-
next: function () {
|
|
59
|
-
if (o && i >= o.length) o = void 0;
|
|
60
|
-
return { value: o && o[i++], done: !o };
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
64
|
-
};
|
|
65
65
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
66
66
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
67
67
|
};
|
|
68
68
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
69
69
|
var path_1 = __importDefault(require("path"));
|
|
70
70
|
var api_1 = require("@sasjs/api");
|
|
71
|
+
var yazl_1 = __importDefault(require("yazl"));
|
|
71
72
|
/**
|
|
72
73
|
* The other half of the parity claim: the service worker is what turns an
|
|
73
74
|
* /SASjsApi/ request into a response, so its framing (webout body, the _debug
|
|
@@ -190,6 +191,24 @@ var jsonResponse = function (body) { return ({
|
|
|
190
191
|
return [2 /*return*/, body];
|
|
191
192
|
}); }); }
|
|
192
193
|
}); };
|
|
194
|
+
/** A zip archive of text members, written the way a deploy upload ships one.
|
|
195
|
+
* The first member is stored rather than deflated, so both methods the reader
|
|
196
|
+
* accepts are exercised by the one case. */
|
|
197
|
+
var zipOf = function (files) {
|
|
198
|
+
return new Promise(function (resolve, reject) {
|
|
199
|
+
var zip = new yazl_1.default.ZipFile();
|
|
200
|
+
Object.keys(files).forEach(function (name, index) {
|
|
201
|
+
return zip.addBuffer(Buffer.from(files[name]), name, {
|
|
202
|
+
compress: index !== 0
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
zip.end();
|
|
206
|
+
var chunks = [];
|
|
207
|
+
zip.outputStream.on('data', function (chunk) { return chunks.push(chunk); });
|
|
208
|
+
zip.outputStream.on('end', function () { return resolve(Buffer.concat(chunks)); });
|
|
209
|
+
zip.outputStream.on('error', reject);
|
|
210
|
+
});
|
|
211
|
+
};
|
|
193
212
|
var runRequest = function (body) { return ({
|
|
194
213
|
method: 'POST',
|
|
195
214
|
headers: { 'content-type': 'application/json' },
|
|
@@ -551,31 +570,424 @@ describe('service worker host', function () {
|
|
|
551
570
|
}
|
|
552
571
|
});
|
|
553
572
|
}); });
|
|
554
|
-
it('answers the session
|
|
555
|
-
var dispatch, session, _a, unknown,
|
|
556
|
-
return __generator(this, function (
|
|
557
|
-
switch (
|
|
573
|
+
it('answers the session route with the caller, and names a route it does not have', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
574
|
+
var dispatch, session, _a, refused, _b, unknown, _c;
|
|
575
|
+
return __generator(this, function (_d) {
|
|
576
|
+
switch (_d.label) {
|
|
558
577
|
case 0:
|
|
559
578
|
dispatch = seeded().dispatch;
|
|
560
579
|
return [4 /*yield*/, dispatch('/SASjsApi/session')];
|
|
561
580
|
case 1:
|
|
562
|
-
session =
|
|
581
|
+
session = _d.sent();
|
|
563
582
|
_a = expect;
|
|
564
583
|
return [4 /*yield*/, session.json()];
|
|
565
584
|
case 2:
|
|
566
|
-
_a.apply(void 0, [
|
|
567
|
-
|
|
585
|
+
_a.apply(void 0, [_d.sent()]).toEqual({
|
|
586
|
+
uid: 'user-admin',
|
|
587
|
+
username: 'sasjsadmin',
|
|
588
|
+
displayName: 'Demo Admin',
|
|
589
|
+
isAdmin: true,
|
|
590
|
+
needsToUpdatePassword: false
|
|
591
|
+
});
|
|
592
|
+
return [4 /*yield*/, dispatch('/SASjsApi/session', {
|
|
593
|
+
headers: { authorization: 'Bearer not-a-token' }
|
|
594
|
+
})];
|
|
568
595
|
case 3:
|
|
569
|
-
|
|
570
|
-
expect(
|
|
596
|
+
refused = _d.sent();
|
|
597
|
+
expect(refused.status).toBe(401);
|
|
571
598
|
_b = expect;
|
|
572
|
-
return [4 /*yield*/,
|
|
599
|
+
return [4 /*yield*/, refused.json()];
|
|
573
600
|
case 4:
|
|
574
|
-
_b.apply(void 0, [(
|
|
601
|
+
_b.apply(void 0, [(_d.sent()).message]).toBe('Unauthorized');
|
|
602
|
+
return [4 /*yield*/, dispatch('/SASjsApi/unknown/route')];
|
|
603
|
+
case 5:
|
|
604
|
+
unknown = _d.sent();
|
|
605
|
+
expect(unknown.status).toBe(404);
|
|
606
|
+
_c = expect;
|
|
607
|
+
return [4 /*yield*/, unknown.json()];
|
|
608
|
+
case 6:
|
|
609
|
+
_c.apply(void 0, [(_d.sent()).message]).toContain('Not part of the API');
|
|
575
610
|
return [2 /*return*/];
|
|
576
611
|
}
|
|
577
612
|
});
|
|
578
613
|
}); });
|
|
614
|
+
describe('the API surface the package carries', function () {
|
|
615
|
+
it('answers the sign-in flow, and the record routes as that caller', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
616
|
+
var worker, signedIn, _a, refused, _b, authorized, code, exchanged, pair, users, _c;
|
|
617
|
+
return __generator(this, function (_d) {
|
|
618
|
+
switch (_d.label) {
|
|
619
|
+
case 0:
|
|
620
|
+
worker = seeded();
|
|
621
|
+
return [4 /*yield*/, worker.dispatch('/SASLogon/login', runRequest({ username: 'sasjsadmin', password: 'Orion123' }))];
|
|
622
|
+
case 1:
|
|
623
|
+
signedIn = _d.sent();
|
|
624
|
+
expect(signedIn.status).toBe(200);
|
|
625
|
+
_a = expect;
|
|
626
|
+
return [4 /*yield*/, signedIn.json()];
|
|
627
|
+
case 2:
|
|
628
|
+
_a.apply(void 0, [(_d.sent()).user.username]).toBe('sasjsadmin');
|
|
629
|
+
return [4 /*yield*/, worker.dispatch('/SASLogon/login', runRequest({ username: 'sasjsadmin', password: 'nope' }))];
|
|
630
|
+
case 3:
|
|
631
|
+
refused = _d.sent();
|
|
632
|
+
expect(refused.status).toBe(401);
|
|
633
|
+
_b = expect;
|
|
634
|
+
return [4 /*yield*/, refused.json()];
|
|
635
|
+
case 4:
|
|
636
|
+
_b.apply(void 0, [(_d.sent()).message]).toBe('Invalid Password.');
|
|
637
|
+
return [4 /*yield*/, worker.dispatch('/SASLogon/authorize', runRequest({ clientId: 'web_app' }))];
|
|
638
|
+
case 5:
|
|
639
|
+
authorized = _d.sent();
|
|
640
|
+
return [4 /*yield*/, authorized.json()];
|
|
641
|
+
case 6:
|
|
642
|
+
code = (_d.sent()).code;
|
|
643
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/auth/token', runRequest({ clientId: 'web_app', code: code }))];
|
|
644
|
+
case 7:
|
|
645
|
+
exchanged = _d.sent();
|
|
646
|
+
return [4 /*yield*/, exchanged.json()];
|
|
647
|
+
case 8:
|
|
648
|
+
pair = _d.sent();
|
|
649
|
+
expect(pair.accessToken).toBeTruthy();
|
|
650
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/user', {
|
|
651
|
+
headers: { authorization: "Bearer ".concat(pair.accessToken) }
|
|
652
|
+
})];
|
|
653
|
+
case 9:
|
|
654
|
+
users = _d.sent();
|
|
655
|
+
_c = expect;
|
|
656
|
+
return [4 /*yield*/, users.json()];
|
|
657
|
+
case 10:
|
|
658
|
+
_c.apply(void 0, [(_d.sent()).map(function (user) { return user.username; }).sort()]).toEqual(['sasjsadmin', 'sasjsuser']);
|
|
659
|
+
return [2 /*return*/];
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
}); });
|
|
663
|
+
it('answers the administration records the fixture seeds', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
664
|
+
var dispatch, groups, _a, permissions, _b, clients, _c, mine, _d, _e, _f;
|
|
665
|
+
return __generator(this, function (_g) {
|
|
666
|
+
switch (_g.label) {
|
|
667
|
+
case 0:
|
|
668
|
+
dispatch = seeded().dispatch;
|
|
669
|
+
return [4 /*yield*/, dispatch('/SASjsApi/group')];
|
|
670
|
+
case 1:
|
|
671
|
+
groups = _g.sent();
|
|
672
|
+
_a = expect;
|
|
673
|
+
return [4 /*yield*/, groups.json()];
|
|
674
|
+
case 2:
|
|
675
|
+
_a.apply(void 0, [(_g.sent()).map(function (group) { return group.name; }).sort()]).toEqual(['Admins', 'DCGroup']);
|
|
676
|
+
return [4 /*yield*/, dispatch('/SASjsApi/permission')];
|
|
677
|
+
case 3:
|
|
678
|
+
permissions = _g.sent();
|
|
679
|
+
_b = expect;
|
|
680
|
+
return [4 /*yield*/, permissions.json()];
|
|
681
|
+
case 4:
|
|
682
|
+
_b.apply(void 0, [(_g.sent()).length]).toBe(3);
|
|
683
|
+
return [4 /*yield*/, dispatch('/SASjsApi/client')];
|
|
684
|
+
case 5:
|
|
685
|
+
clients = _g.sent();
|
|
686
|
+
_c = expect;
|
|
687
|
+
return [4 /*yield*/, clients.json()];
|
|
688
|
+
case 6:
|
|
689
|
+
_c.apply(void 0, [(_g.sent()).map(function (client) { return client.clientId; }).sort()]).toEqual(['sasjs', 'web_app']);
|
|
690
|
+
return [4 /*yield*/, dispatch('/SASjsApi/info/myAuthorizedRoutes')];
|
|
691
|
+
case 7:
|
|
692
|
+
mine = _g.sent();
|
|
693
|
+
_d = expect;
|
|
694
|
+
_f = (_e = Object).keys;
|
|
695
|
+
return [4 /*yield*/, mine.json()];
|
|
696
|
+
case 8:
|
|
697
|
+
_d.apply(void 0, [_f.apply(_e, [_g.sent()]).length]).toBeGreaterThan(0);
|
|
698
|
+
return [2 /*return*/];
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
}); });
|
|
702
|
+
it('answers the documented 501 for the capabilities a static origin lacks', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
703
|
+
var dispatch, cases, cases_1, cases_1_1, _a, method, url, capability, response, _b, e_1_1, _c, _d, url, response, _e, e_2_1;
|
|
704
|
+
var e_1, _f, e_2, _g;
|
|
705
|
+
return __generator(this, function (_h) {
|
|
706
|
+
switch (_h.label) {
|
|
707
|
+
case 0:
|
|
708
|
+
dispatch = seeded().dispatch;
|
|
709
|
+
cases = [
|
|
710
|
+
['GET', '/SASjsApi/admin/diagnostics', 'admin service'],
|
|
711
|
+
['GET', '/SASjsApi/admin/logs', 'admin service'],
|
|
712
|
+
['GET', '/SASjsApi/admin/logs/app.log', 'admin service'],
|
|
713
|
+
[
|
|
714
|
+
'POST',
|
|
715
|
+
'/SASjsApi/authConfig/synchroniseWithLDAP',
|
|
716
|
+
'directory service'
|
|
717
|
+
]
|
|
718
|
+
];
|
|
719
|
+
_h.label = 1;
|
|
720
|
+
case 1:
|
|
721
|
+
_h.trys.push([1, 7, 8, 9]);
|
|
722
|
+
cases_1 = __values(cases), cases_1_1 = cases_1.next();
|
|
723
|
+
_h.label = 2;
|
|
724
|
+
case 2:
|
|
725
|
+
if (!!cases_1_1.done) return [3 /*break*/, 6];
|
|
726
|
+
_a = __read(cases_1_1.value, 3), method = _a[0], url = _a[1], capability = _a[2];
|
|
727
|
+
return [4 /*yield*/, dispatch(url, { method: method })];
|
|
728
|
+
case 3:
|
|
729
|
+
response = _h.sent();
|
|
730
|
+
expect(response.status).toBe(501);
|
|
731
|
+
_b = expect;
|
|
732
|
+
return [4 /*yield*/, response.json()];
|
|
733
|
+
case 4:
|
|
734
|
+
_b.apply(void 0, [(_h.sent()).message]).toContain(capability);
|
|
735
|
+
_h.label = 5;
|
|
736
|
+
case 5:
|
|
737
|
+
cases_1_1 = cases_1.next();
|
|
738
|
+
return [3 /*break*/, 2];
|
|
739
|
+
case 6: return [3 /*break*/, 9];
|
|
740
|
+
case 7:
|
|
741
|
+
e_1_1 = _h.sent();
|
|
742
|
+
e_1 = { error: e_1_1 };
|
|
743
|
+
return [3 /*break*/, 9];
|
|
744
|
+
case 8:
|
|
745
|
+
try {
|
|
746
|
+
if (cases_1_1 && !cases_1_1.done && (_f = cases_1.return)) _f.call(cases_1);
|
|
747
|
+
}
|
|
748
|
+
finally { if (e_1) throw e_1.error; }
|
|
749
|
+
return [7 /*endfinally*/];
|
|
750
|
+
case 9:
|
|
751
|
+
_h.trys.push([9, 15, 16, 17]);
|
|
752
|
+
_c = __values(['/SASjsApi/code/lint', '/SASjsApi/code/format']), _d = _c.next();
|
|
753
|
+
_h.label = 10;
|
|
754
|
+
case 10:
|
|
755
|
+
if (!!_d.done) return [3 /*break*/, 14];
|
|
756
|
+
url = _d.value;
|
|
757
|
+
return [4 /*yield*/, dispatch(url, runRequest({ code: 'data a;' }))];
|
|
758
|
+
case 11:
|
|
759
|
+
response = _h.sent();
|
|
760
|
+
expect(response.status).toBe(501);
|
|
761
|
+
_e = expect;
|
|
762
|
+
return [4 /*yield*/, response.json()];
|
|
763
|
+
case 12:
|
|
764
|
+
_e.apply(void 0, [(_h.sent()).message]).toContain('code service');
|
|
765
|
+
_h.label = 13;
|
|
766
|
+
case 13:
|
|
767
|
+
_d = _c.next();
|
|
768
|
+
return [3 /*break*/, 10];
|
|
769
|
+
case 14: return [3 /*break*/, 17];
|
|
770
|
+
case 15:
|
|
771
|
+
e_2_1 = _h.sent();
|
|
772
|
+
e_2 = { error: e_2_1 };
|
|
773
|
+
return [3 /*break*/, 17];
|
|
774
|
+
case 16:
|
|
775
|
+
try {
|
|
776
|
+
if (_d && !_d.done && (_g = _c.return)) _g.call(_c);
|
|
777
|
+
}
|
|
778
|
+
finally { if (e_2) throw e_2.error; }
|
|
779
|
+
return [7 /*endfinally*/];
|
|
780
|
+
case 17: return [2 /*return*/];
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
}); });
|
|
784
|
+
it('matches a path whatever its casing, as the API does', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
785
|
+
var dispatch, response, _a;
|
|
786
|
+
return __generator(this, function (_b) {
|
|
787
|
+
switch (_b.label) {
|
|
788
|
+
case 0:
|
|
789
|
+
dispatch = seeded().dispatch;
|
|
790
|
+
return [4 /*yield*/, dispatch('/sasjsapi/INFO')];
|
|
791
|
+
case 1:
|
|
792
|
+
response = _b.sent();
|
|
793
|
+
expect(response.status).toBe(200);
|
|
794
|
+
_a = expect;
|
|
795
|
+
return [4 /*yield*/, response.json()];
|
|
796
|
+
case 2:
|
|
797
|
+
_a.apply(void 0, [(_b.sent()).mode]).toBe('desktop');
|
|
798
|
+
return [2 /*return*/];
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
}); });
|
|
802
|
+
it('starts a session, then reads its state, log and output', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
803
|
+
var dispatch, program, triggered, sessionId, state, _a, output, _b, log, missing;
|
|
804
|
+
return __generator(this, function (_c) {
|
|
805
|
+
switch (_c.label) {
|
|
806
|
+
case 0:
|
|
807
|
+
dispatch = seeded().dispatch;
|
|
808
|
+
program = encodeURIComponent("".concat(appLoc, "/services/public/getdata"));
|
|
809
|
+
return [4 /*yield*/, dispatch('/SASjsApi/stp/trigger?_program=' + program, runRequest({ params: {} }))];
|
|
810
|
+
case 1:
|
|
811
|
+
triggered = _c.sent();
|
|
812
|
+
expect(triggered.status).toBe(200);
|
|
813
|
+
return [4 /*yield*/, triggered.json()];
|
|
814
|
+
case 2:
|
|
815
|
+
sessionId = (_c.sent()).sessionId;
|
|
816
|
+
expect(sessionId).toBeTruthy();
|
|
817
|
+
return [4 /*yield*/, dispatch("/SASjsApi/session/".concat(sessionId, "/state"))];
|
|
818
|
+
case 3:
|
|
819
|
+
state = _c.sent();
|
|
820
|
+
_a = expect;
|
|
821
|
+
return [4 /*yield*/, state.json()];
|
|
822
|
+
case 4:
|
|
823
|
+
_a.apply(void 0, [_c.sent()]).toEqual({ state: 'completed' });
|
|
824
|
+
return [4 /*yield*/, dispatch("/SASjsApi/session/".concat(sessionId, "/output"))];
|
|
825
|
+
case 5:
|
|
826
|
+
output = _c.sent();
|
|
827
|
+
expect(output.status).toBe(200);
|
|
828
|
+
_b = expect;
|
|
829
|
+
return [4 /*yield*/, output.text()];
|
|
830
|
+
case 6:
|
|
831
|
+
_b.apply(void 0, [_c.sent()]).toBe('seeded');
|
|
832
|
+
return [4 /*yield*/, dispatch("/SASjsApi/session/".concat(sessionId, "/log"))];
|
|
833
|
+
case 7:
|
|
834
|
+
log = _c.sent();
|
|
835
|
+
expect(log.status).toBe(200);
|
|
836
|
+
return [4 /*yield*/, dispatch('/SASjsApi/session/not-a-session/state')];
|
|
837
|
+
case 8:
|
|
838
|
+
missing = _c.sent();
|
|
839
|
+
expect(missing.status).toBe(404);
|
|
840
|
+
return [2 /*return*/];
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
}); });
|
|
844
|
+
it('refuses a deploy upload with no archive, and one that is not a zip', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
845
|
+
var dispatch, empty, _a, form, notZip, _b;
|
|
846
|
+
return __generator(this, function (_c) {
|
|
847
|
+
switch (_c.label) {
|
|
848
|
+
case 0:
|
|
849
|
+
dispatch = seeded().dispatch;
|
|
850
|
+
return [4 /*yield*/, dispatch('/SASjsApi/drive/deploy/upload', {
|
|
851
|
+
method: 'POST',
|
|
852
|
+
body: new FormData()
|
|
853
|
+
})];
|
|
854
|
+
case 1:
|
|
855
|
+
empty = _c.sent();
|
|
856
|
+
expect(empty.status).toBe(400);
|
|
857
|
+
_a = expect;
|
|
858
|
+
return [4 /*yield*/, empty.text()];
|
|
859
|
+
case 2:
|
|
860
|
+
_a.apply(void 0, [_c.sent()]).toBe('"file" is not present.');
|
|
861
|
+
form = new FormData();
|
|
862
|
+
form.set('file', new Blob([new Uint8Array([1, 2, 3])]), 'not-a-zip.zip');
|
|
863
|
+
return [4 /*yield*/, dispatch('/SASjsApi/drive/deploy/upload', {
|
|
864
|
+
method: 'POST',
|
|
865
|
+
body: form
|
|
866
|
+
})];
|
|
867
|
+
case 3:
|
|
868
|
+
notZip = _c.sent();
|
|
869
|
+
expect(notZip.status).toBe(400);
|
|
870
|
+
_b = expect;
|
|
871
|
+
return [4 /*yield*/, notZip.text()];
|
|
872
|
+
case 4:
|
|
873
|
+
_b.apply(void 0, [_c.sent()]).toBe('The archive is not a zip file.');
|
|
874
|
+
return [2 /*return*/];
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
}); });
|
|
878
|
+
it('takes a token from the query when a link cannot carry a header', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
879
|
+
var worker, authorized, code, exchanged, pair, users, _a;
|
|
880
|
+
return __generator(this, function (_b) {
|
|
881
|
+
switch (_b.label) {
|
|
882
|
+
case 0:
|
|
883
|
+
worker = seeded();
|
|
884
|
+
return [4 /*yield*/, worker.dispatch('/SASLogon/authorize', runRequest({ clientId: 'web_app' }))];
|
|
885
|
+
case 1:
|
|
886
|
+
authorized = _b.sent();
|
|
887
|
+
return [4 /*yield*/, authorized.json()];
|
|
888
|
+
case 2:
|
|
889
|
+
code = (_b.sent()).code;
|
|
890
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/auth/token', runRequest({ clientId: 'web_app', code: code }))];
|
|
891
|
+
case 3:
|
|
892
|
+
exchanged = _b.sent();
|
|
893
|
+
return [4 /*yield*/, exchanged.json()];
|
|
894
|
+
case 4:
|
|
895
|
+
pair = _b.sent();
|
|
896
|
+
return [4 /*yield*/, worker.dispatch("/SASjsApi/user?access_token=".concat(encodeURIComponent(pair.accessToken)))];
|
|
897
|
+
case 5:
|
|
898
|
+
users = _b.sent();
|
|
899
|
+
expect(users.status).toBe(200);
|
|
900
|
+
_a = expect;
|
|
901
|
+
return [4 /*yield*/, users.json()];
|
|
902
|
+
case 6:
|
|
903
|
+
_a.apply(void 0, [(_b.sent()).length]).toBe(2);
|
|
904
|
+
return [2 /*return*/];
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
}); });
|
|
908
|
+
it('names a table the page did not name, and takes an archive under any field', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
909
|
+
var worker, unnamed, _a, form, _b, _c, _d, _e, _f, upload;
|
|
910
|
+
var _g;
|
|
911
|
+
return __generator(this, function (_h) {
|
|
912
|
+
switch (_h.label) {
|
|
913
|
+
case 0:
|
|
914
|
+
worker = seeded();
|
|
915
|
+
worker.runtime.load((_g = {},
|
|
916
|
+
_g[drivePath('services/public/echo.js')] = '_webout = _WEBIN_NAME1 + "|" + _WEBIN_FILEREF1;',
|
|
917
|
+
_g));
|
|
918
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/stp/execute?_program=' +
|
|
919
|
+
encodeURIComponent("".concat(appLoc, "/services/public/echo")), runRequest({ params: {}, tables: [{ csv: 'A,B\r\n1,2' }] }))];
|
|
920
|
+
case 1:
|
|
921
|
+
unnamed = _h.sent();
|
|
922
|
+
_a = expect;
|
|
923
|
+
return [4 /*yield*/, unnamed.text()];
|
|
924
|
+
case 2:
|
|
925
|
+
_a.apply(void 0, [_h.sent()]).toBe('table1|A,B\r\n1,2');
|
|
926
|
+
form = new FormData();
|
|
927
|
+
form.set('appLoc', appLoc);
|
|
928
|
+
_c = (_b = form).set;
|
|
929
|
+
_d = ['archive'];
|
|
930
|
+
_e = Blob.bind;
|
|
931
|
+
_f = Uint8Array.bind;
|
|
932
|
+
return [4 /*yield*/, zipOf({ 'x.txt': 'x' })];
|
|
933
|
+
case 3:
|
|
934
|
+
_c.apply(_b, _d.concat([new (_e.apply(Blob, [void 0, [new (_f.apply(Uint8Array, [void 0, _h.sent()]))()]]))(),
|
|
935
|
+
'deploy.zip']));
|
|
936
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/drive/deploy/upload', {
|
|
937
|
+
method: 'POST',
|
|
938
|
+
body: form
|
|
939
|
+
})];
|
|
940
|
+
case 4:
|
|
941
|
+
upload = _h.sent();
|
|
942
|
+
expect(upload.status).toBe(200);
|
|
943
|
+
return [2 /*return*/];
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
}); });
|
|
947
|
+
it('expands an uploaded archive into the drive', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
948
|
+
var dispatch, archive, form, response, tree, listing, _a, _b, file, _c;
|
|
949
|
+
return __generator(this, function (_d) {
|
|
950
|
+
switch (_d.label) {
|
|
951
|
+
case 0:
|
|
952
|
+
dispatch = seeded().dispatch;
|
|
953
|
+
return [4 /*yield*/, zipOf({
|
|
954
|
+
'services/public/run.js': '_webout = "deployed";',
|
|
955
|
+
'docs/readme.txt': 'hello'
|
|
956
|
+
})];
|
|
957
|
+
case 1:
|
|
958
|
+
archive = _d.sent();
|
|
959
|
+
form = new FormData();
|
|
960
|
+
form.set('appLoc', appLoc);
|
|
961
|
+
form.set('file', new Blob([new Uint8Array(archive)]), 'deploy.zip');
|
|
962
|
+
return [4 /*yield*/, dispatch('/SASjsApi/drive/deploy/upload', {
|
|
963
|
+
method: 'POST',
|
|
964
|
+
body: form
|
|
965
|
+
})];
|
|
966
|
+
case 2:
|
|
967
|
+
response = _d.sent();
|
|
968
|
+
expect(response.status).toBe(200);
|
|
969
|
+
return [4 /*yield*/, dispatch('/SASjsApi/drive/filetree?_folderPath=' + encodeURIComponent(appLoc))];
|
|
970
|
+
case 3:
|
|
971
|
+
tree = _d.sent();
|
|
972
|
+
_b = (_a = JSON).stringify;
|
|
973
|
+
return [4 /*yield*/, tree.json()];
|
|
974
|
+
case 4:
|
|
975
|
+
listing = _b.apply(_a, [_d.sent()]);
|
|
976
|
+
expect(listing).toContain('run.js');
|
|
977
|
+
expect(listing).toContain('readme.txt');
|
|
978
|
+
return [4 /*yield*/, dispatch('/SASjsApi/drive/file?_filePath=' +
|
|
979
|
+
encodeURIComponent("".concat(appLoc, "/docs/readme.txt")))];
|
|
980
|
+
case 5:
|
|
981
|
+
file = _d.sent();
|
|
982
|
+
_c = expect;
|
|
983
|
+
return [4 /*yield*/, file.text()];
|
|
984
|
+
case 6:
|
|
985
|
+
_c.apply(void 0, [_d.sent()]).toBe('hello');
|
|
986
|
+
return [2 /*return*/];
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
}); });
|
|
990
|
+
});
|
|
579
991
|
it('answers 404 for a drive file the drive does not hold', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
580
992
|
var dispatch, response, _a;
|
|
581
993
|
return __generator(this, function (_b) {
|
|
@@ -686,12 +1098,12 @@ describe('service worker host', function () {
|
|
|
686
1098
|
_a = expect;
|
|
687
1099
|
return [4 /*yield*/, response.json()];
|
|
688
1100
|
case 2:
|
|
689
|
-
_a.apply(void 0, [_b.sent()]).toEqual({ message: '_program is required' });
|
|
1101
|
+
_a.apply(void 0, [_b.sent()]).toEqual({ message: '"_program" is required' });
|
|
690
1102
|
return [2 /*return*/];
|
|
691
1103
|
}
|
|
692
1104
|
});
|
|
693
1105
|
}); });
|
|
694
|
-
it('answers
|
|
1106
|
+
it('answers the documented failure envelope when the service throws', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
695
1107
|
var _a, runtime, dispatch, response, body;
|
|
696
1108
|
var _b;
|
|
697
1109
|
return __generator(this, function (_c) {
|
|
@@ -705,11 +1117,17 @@ describe('service worker host', function () {
|
|
|
705
1117
|
encodeURIComponent("".concat(appLoc, "/services/public/broken")))];
|
|
706
1118
|
case 1:
|
|
707
1119
|
response = _c.sent();
|
|
708
|
-
return [4 /*yield*/, response.json()
|
|
1120
|
+
return [4 /*yield*/, response.json()
|
|
1121
|
+
// the package's decision, and the server's: a run that failed is a 400
|
|
1122
|
+
// carrying the failure envelope, not a 500 that blames the transport
|
|
1123
|
+
];
|
|
709
1124
|
case 2:
|
|
710
1125
|
body = _c.sent();
|
|
711
|
-
|
|
1126
|
+
// the package's decision, and the server's: a run that failed is a 400
|
|
1127
|
+
// carrying the failure envelope, not a 500 that blames the transport
|
|
1128
|
+
expect(response.status).toBe(400);
|
|
712
1129
|
expect(body.status).toBe('failure');
|
|
1130
|
+
expect(body.message).toBe('Job execution failed.');
|
|
713
1131
|
expect(body.error).toContain('boom');
|
|
714
1132
|
return [2 /*return*/];
|
|
715
1133
|
}
|
|
@@ -721,13 +1139,58 @@ describe('service worker host', function () {
|
|
|
721
1139
|
switch (_b.label) {
|
|
722
1140
|
case 0:
|
|
723
1141
|
dispatch = seeded().dispatch;
|
|
724
|
-
return [4 /*yield*/, dispatch('/SASjsApi/code/execute', runRequest({ code: '_webout = "inline";', params: {} }))
|
|
1142
|
+
return [4 /*yield*/, dispatch('/SASjsApi/code/execute', runRequest({ code: '_webout = "inline";', runTime: 'js', params: {} }))
|
|
1143
|
+
// a code route's documented body is the webout framed with the log, so the
|
|
1144
|
+
// run's output is the first section rather than the whole body
|
|
1145
|
+
];
|
|
725
1146
|
case 1:
|
|
726
1147
|
response = _b.sent();
|
|
1148
|
+
// a code route's documented body is the webout framed with the log, so the
|
|
1149
|
+
// run's output is the first section rather than the whole body
|
|
1150
|
+
_a = expect;
|
|
1151
|
+
return [4 /*yield*/, response.text()];
|
|
1152
|
+
case 2:
|
|
1153
|
+
// a code route's documented body is the webout framed with the log, so the
|
|
1154
|
+
// run's output is the first section rather than the whole body
|
|
1155
|
+
_a.apply(void 0, [(_b.sent()).split(LOGS_SEPARATOR)[0].trim()]).toBe('inline');
|
|
1156
|
+
return [2 /*return*/];
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
}); });
|
|
1160
|
+
it('reads the table envelope the page bridge posts for an XHR', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1161
|
+
var worker, response, _a;
|
|
1162
|
+
var _b;
|
|
1163
|
+
return __generator(this, function (_c) {
|
|
1164
|
+
switch (_c.label) {
|
|
1165
|
+
case 0:
|
|
1166
|
+
worker = seeded();
|
|
1167
|
+
worker.runtime.load((_b = {},
|
|
1168
|
+
_b[drivePath('services/public/echo.js')] = '_webout = _WEBIN_NAME1 + "|" + _WEBIN_FILEREF1;',
|
|
1169
|
+
_b));
|
|
1170
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/stp/execute?_program=' +
|
|
1171
|
+
encodeURIComponent("".concat(appLoc, "/services/public/echo")), runRequest({
|
|
1172
|
+
params: {},
|
|
1173
|
+
tables: [
|
|
1174
|
+
{
|
|
1175
|
+
name: 'SASControlTable',
|
|
1176
|
+
filename: 'SASControlTable.csv',
|
|
1177
|
+
csv: 'A,B\r\n1,2'
|
|
1178
|
+
}
|
|
1179
|
+
]
|
|
1180
|
+
}))
|
|
1181
|
+
// the bridge hands the worker `{params, tables}`; a stored program sees the
|
|
1182
|
+
// tables as its _WEBIN variables, not as one `tables` session variable
|
|
1183
|
+
];
|
|
1184
|
+
case 1:
|
|
1185
|
+
response = _c.sent();
|
|
1186
|
+
// the bridge hands the worker `{params, tables}`; a stored program sees the
|
|
1187
|
+
// tables as its _WEBIN variables, not as one `tables` session variable
|
|
727
1188
|
_a = expect;
|
|
728
1189
|
return [4 /*yield*/, response.text()];
|
|
729
1190
|
case 2:
|
|
730
|
-
|
|
1191
|
+
// the bridge hands the worker `{params, tables}`; a stored program sees the
|
|
1192
|
+
// tables as its _WEBIN variables, not as one `tables` session variable
|
|
1193
|
+
_a.apply(void 0, [_c.sent()]).toBe('SASControlTable|A,B\r\n1,2');
|
|
731
1194
|
return [2 /*return*/];
|
|
732
1195
|
}
|
|
733
1196
|
});
|
|
@@ -1393,7 +1856,7 @@ describe('service worker host', function () {
|
|
|
1393
1856
|
}); });
|
|
1394
1857
|
});
|
|
1395
1858
|
describe('conformance corpus', function () {
|
|
1396
|
-
var
|
|
1859
|
+
var e_3, _a;
|
|
1397
1860
|
/**
|
|
1398
1861
|
* The corpus is what "the same API" means: the cases @sasjs/api runs against
|
|
1399
1862
|
* its own services, and against a live sasjs/server, run here against this
|
|
@@ -1402,7 +1865,7 @@ describe('service worker host', function () {
|
|
|
1402
1865
|
* used to have - and it is a test rather than a review.
|
|
1403
1866
|
*/
|
|
1404
1867
|
var httpFor = function (testCase) {
|
|
1405
|
-
var
|
|
1868
|
+
var e_4, _a;
|
|
1406
1869
|
var _b;
|
|
1407
1870
|
var _c = __read(testCase.route.split(' '), 2), method = _c[0], routePath = _c[1];
|
|
1408
1871
|
var query = new URLSearchParams();
|
|
@@ -1413,12 +1876,12 @@ describe('service worker host', function () {
|
|
|
1413
1876
|
query.set(key, String(value));
|
|
1414
1877
|
}
|
|
1415
1878
|
}
|
|
1416
|
-
catch (
|
|
1879
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
1417
1880
|
finally {
|
|
1418
1881
|
try {
|
|
1419
1882
|
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
|
|
1420
1883
|
}
|
|
1421
|
-
finally { if (
|
|
1884
|
+
finally { if (e_4) throw e_4.error; }
|
|
1422
1885
|
}
|
|
1423
1886
|
var suffix = query.toString() ? "?".concat(query) : '';
|
|
1424
1887
|
var init = { method: method };
|
|
@@ -1449,8 +1912,8 @@ describe('service worker host', function () {
|
|
|
1449
1912
|
* does.
|
|
1450
1913
|
*/
|
|
1451
1914
|
var seedDrive = function (runtime, testCase) { return __awaiter(void 0, void 0, void 0, function () {
|
|
1452
|
-
var text, _a, _b, _c, file, contents, _d, _e, _f, file, contents,
|
|
1453
|
-
var
|
|
1915
|
+
var text, _a, _b, _c, file, contents, _d, _e, _f, file, contents, e_5_1;
|
|
1916
|
+
var e_6, _g, e_5, _h;
|
|
1454
1917
|
return __generator(this, function (_j) {
|
|
1455
1918
|
switch (_j.label) {
|
|
1456
1919
|
case 0:
|
|
@@ -1462,12 +1925,12 @@ describe('service worker host', function () {
|
|
|
1462
1925
|
text["/sasjs/drive/files".concat(file)] = contents;
|
|
1463
1926
|
}
|
|
1464
1927
|
}
|
|
1465
|
-
catch (
|
|
1928
|
+
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
|
1466
1929
|
finally {
|
|
1467
1930
|
try {
|
|
1468
1931
|
if (_b && !_b.done && (_g = _a.return)) _g.call(_a);
|
|
1469
1932
|
}
|
|
1470
|
-
finally { if (
|
|
1933
|
+
finally { if (e_6) throw e_6.error; }
|
|
1471
1934
|
}
|
|
1472
1935
|
return [4 /*yield*/, runtime.load(text)];
|
|
1473
1936
|
case 1:
|
|
@@ -1490,14 +1953,14 @@ describe('service worker host', function () {
|
|
|
1490
1953
|
return [3 /*break*/, 3];
|
|
1491
1954
|
case 6: return [3 /*break*/, 9];
|
|
1492
1955
|
case 7:
|
|
1493
|
-
|
|
1494
|
-
|
|
1956
|
+
e_5_1 = _j.sent();
|
|
1957
|
+
e_5 = { error: e_5_1 };
|
|
1495
1958
|
return [3 /*break*/, 9];
|
|
1496
1959
|
case 8:
|
|
1497
1960
|
try {
|
|
1498
1961
|
if (_e && !_e.done && (_h = _d.return)) _h.call(_d);
|
|
1499
1962
|
}
|
|
1500
|
-
finally { if (
|
|
1963
|
+
finally { if (e_5) throw e_5.error; }
|
|
1501
1964
|
return [7 /*endfinally*/];
|
|
1502
1965
|
case 9: return [2 /*return*/];
|
|
1503
1966
|
}
|
|
@@ -1505,42 +1968,55 @@ describe('service worker host', function () {
|
|
|
1505
1968
|
}); };
|
|
1506
1969
|
var _loop_1 = function (testCase) {
|
|
1507
1970
|
it(testCase.id, function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1508
|
-
var worker, _a, requestPath, init, response,
|
|
1509
|
-
return __generator(this, function (
|
|
1510
|
-
switch (
|
|
1971
|
+
var worker, _a, requestPath, init, response, body, want, _b, _c, _d;
|
|
1972
|
+
return __generator(this, function (_e) {
|
|
1973
|
+
switch (_e.label) {
|
|
1511
1974
|
case 0:
|
|
1512
1975
|
worker = loadWorker();
|
|
1513
1976
|
return [4 /*yield*/, seedDrive(worker.runtime, testCase)];
|
|
1514
1977
|
case 1:
|
|
1515
|
-
|
|
1978
|
+
_e.sent();
|
|
1516
1979
|
worker.refuseWrites(Boolean(testCase.unwritable));
|
|
1517
1980
|
_a = httpFor(testCase), requestPath = _a.path, init = _a.init;
|
|
1518
1981
|
return [4 /*yield*/, worker.dispatch(requestPath, init)];
|
|
1519
1982
|
case 2:
|
|
1520
|
-
response =
|
|
1983
|
+
response = _e.sent();
|
|
1521
1984
|
expect(response.status).toBe(testCase.expect.status);
|
|
1522
1985
|
if (!(testCase.expect.error !== undefined)) return [3 /*break*/, 4];
|
|
1523
|
-
|
|
1524
|
-
|
|
1986
|
+
return [4 /*yield*/, response.text()
|
|
1987
|
+
// the routes that answer an envelope - the two deploy routes - send
|
|
1988
|
+
// `{status, message}`; the rest send the message itself as text, which
|
|
1989
|
+
// is what the server's `res.status(code).send(err.message)` does
|
|
1990
|
+
];
|
|
1525
1991
|
case 3:
|
|
1526
|
-
|
|
1527
|
-
|
|
1992
|
+
body = _e.sent();
|
|
1993
|
+
// the routes that answer an envelope - the two deploy routes - send
|
|
1994
|
+
// `{status, message}`; the rest send the message itself as text, which
|
|
1995
|
+
// is what the server's `res.status(code).send(err.message)` does
|
|
1996
|
+
if ((response.headers.get('content-type') || '').includes('application/json'))
|
|
1997
|
+
expect(JSON.parse(body)).toEqual({
|
|
1998
|
+
status: 'failure',
|
|
1999
|
+
message: testCase.expect.error
|
|
2000
|
+
});
|
|
2001
|
+
else
|
|
2002
|
+
expect(body).toBe(testCase.expect.error);
|
|
2003
|
+
return [3 /*break*/, 8];
|
|
1528
2004
|
case 4:
|
|
1529
2005
|
want = testCase.expect.contents;
|
|
1530
2006
|
if (!(typeof want === 'string')) return [3 /*break*/, 6];
|
|
1531
|
-
|
|
2007
|
+
_b = expect;
|
|
1532
2008
|
return [4 /*yield*/, response.text()];
|
|
1533
2009
|
case 5:
|
|
1534
|
-
|
|
2010
|
+
_b.apply(void 0, [_e.sent()]).toBe(want);
|
|
1535
2011
|
return [3 /*break*/, 8];
|
|
1536
2012
|
case 6:
|
|
1537
2013
|
if (!(want !== undefined)) return [3 /*break*/, 8];
|
|
1538
|
-
|
|
1539
|
-
|
|
2014
|
+
_c = expect;
|
|
2015
|
+
_d = Uint8Array.bind;
|
|
1540
2016
|
return [4 /*yield*/, response.arrayBuffer()];
|
|
1541
2017
|
case 7:
|
|
1542
|
-
|
|
1543
|
-
|
|
2018
|
+
_c.apply(void 0, [new (_d.apply(Uint8Array, [void 0, _e.sent()]))()]).toEqual(want);
|
|
2019
|
+
_e.label = 8;
|
|
1544
2020
|
case 8: return [2 /*return*/];
|
|
1545
2021
|
}
|
|
1546
2022
|
});
|
|
@@ -1552,12 +2028,12 @@ describe('service worker host', function () {
|
|
|
1552
2028
|
_loop_1(testCase);
|
|
1553
2029
|
}
|
|
1554
2030
|
}
|
|
1555
|
-
catch (
|
|
2031
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
1556
2032
|
finally {
|
|
1557
2033
|
try {
|
|
1558
2034
|
if (DRIVE_CORPUS_1_1 && !DRIVE_CORPUS_1_1.done && (_a = DRIVE_CORPUS_1.return)) _a.call(DRIVE_CORPUS_1);
|
|
1559
2035
|
}
|
|
1560
|
-
finally { if (
|
|
2036
|
+
finally { if (e_3) throw e_3.error; }
|
|
1561
2037
|
}
|
|
1562
2038
|
});
|
|
1563
2039
|
});
|