@sasjs/cli 6.31.0 → 6.32.1
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/api-console.js +42 -7
- package/build/commands/build/internal/mockSite/assets/api-index.html +1 -1
- package/build/commands/build/internal/mockSite/assets/sw.js +12 -35
- package/build/commands/build/internal/mockSite/mockSite.d.ts +1 -1
- package/build/commands/build/internal/mockSite/mockSite.js +14 -9
- package/build/commands/build/internal/mockSite/spec/apiConsole.spec.js +74 -8
- package/build/commands/build/internal/mockSite/spec/apiConsoleBasePath.spec.js +121 -9
- package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +9 -11
- package/build/commands/build/internal/mockSite/spec/sw.spec.js +74 -94
- package/build/types/mockWebBasePath.d.ts +1 -1
- package/package.json +1 -1
|
@@ -23,13 +23,34 @@
|
|
|
23
23
|
'use strict'
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* The site's root
|
|
27
|
-
*
|
|
26
|
+
* The site's root.
|
|
27
|
+
*
|
|
28
|
+
* Taken from the worker that controls this page, `<base>/sw.js`: that is the
|
|
29
|
+
* one statement of the base the page carries, and it holds wherever the console
|
|
30
|
+
* sits - a copy under `<base>/tools/api-console/` still calls the site's own
|
|
31
|
+
* API rather than a folder that happens to be two levels up. Only a worker
|
|
32
|
+
* whose root covers this page counts, since a page can be controlled by another
|
|
33
|
+
* site's worker, and one registered below the page names a folder inside it. A
|
|
34
|
+
* page with no such worker falls back to its own URL, one level below the root.
|
|
28
35
|
*/
|
|
29
36
|
const base = (function () {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
// this page's own folder, however it was typed: with the page, with the
|
|
38
|
+
// file, or without the trailing slash
|
|
39
|
+
let dir = location.pathname
|
|
40
|
+
if (/\/index\.html?$/.test(dir)) dir = dir.replace(/index\.html?$/, '')
|
|
41
|
+
if (!dir.endsWith('/')) dir += '/'
|
|
42
|
+
const parent = dir.replace(/[^/]+\/$/, '')
|
|
43
|
+
|
|
44
|
+
const script =
|
|
45
|
+
navigator.serviceWorker && navigator.serviceWorker.controller
|
|
46
|
+
? navigator.serviceWorker.controller.scriptURL
|
|
47
|
+
: ''
|
|
48
|
+
if (script) {
|
|
49
|
+
const swPath = new URL(script).pathname
|
|
50
|
+
const root = /\/sw\.js$/.test(swPath) ? swPath.replace(/sw\.js$/, '') : ''
|
|
51
|
+
if (root && parent.startsWith(root)) return root
|
|
52
|
+
}
|
|
53
|
+
return parent || '/'
|
|
33
54
|
})()
|
|
34
55
|
|
|
35
56
|
const apiUrl = (path) => base + String(path).replace(/^\/+/, '')
|
|
@@ -76,7 +97,7 @@
|
|
|
76
97
|
* Starts the demo backend for this page.
|
|
77
98
|
*
|
|
78
99
|
* The application's boot loader registers the worker; a visitor who lands on
|
|
79
|
-
* `/api/` first has no loader, and every request would reach the static host
|
|
100
|
+
* `/api-console/` first has no loader, and every request would reach the static host
|
|
80
101
|
* and 404. So: register it beside the site, wait for it to control this page,
|
|
81
102
|
* then load `host/persist.js`, which is what hands the worker the drive this
|
|
82
103
|
* browser already has and keeps whatever this page changes.
|
|
@@ -692,6 +713,14 @@
|
|
|
692
713
|
)
|
|
693
714
|
})
|
|
694
715
|
|
|
716
|
+
/** Where a reset returns to: this page, without the flag but with the route. */
|
|
717
|
+
const resetTarget = () => {
|
|
718
|
+
const query = new URLSearchParams(location.search)
|
|
719
|
+
query.delete('reset')
|
|
720
|
+
const rest = query.toString()
|
|
721
|
+
return location.pathname + (rest ? '?' + rest : '')
|
|
722
|
+
}
|
|
723
|
+
|
|
695
724
|
/**
|
|
696
725
|
* `?reset=1` starts the demo again from the state the build ships.
|
|
697
726
|
*
|
|
@@ -719,7 +748,9 @@
|
|
|
719
748
|
'The demo could not be reset: ' + ((e && e.message) || e)
|
|
720
749
|
return true
|
|
721
750
|
}
|
|
722
|
-
|
|
751
|
+
// back to this page, with the flag gone: the console's folder is the page's
|
|
752
|
+
// own, so a build that moves the folder cannot strand the redirect
|
|
753
|
+
location.replace(resetTarget())
|
|
723
754
|
return true
|
|
724
755
|
}
|
|
725
756
|
|
|
@@ -755,6 +786,10 @@
|
|
|
755
786
|
if (chosen) select(chosen)
|
|
756
787
|
}
|
|
757
788
|
|
|
789
|
+
// the redirect itself cannot be observed in a page that does not navigate; the
|
|
790
|
+
// choice of target can, and it is what a folder move would break
|
|
791
|
+
window.SASjsApiConsole = { resetTarget }
|
|
792
|
+
|
|
758
793
|
start().catch((e) => {
|
|
759
794
|
const list = el('routes')
|
|
760
795
|
if (list) list.textContent = 'The console could not start: ' + e.message
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
The API console for a browser-hosted build.
|
|
10
10
|
|
|
11
11
|
Static, and deliberately so: no bundler, no framework, no network of its
|
|
12
|
-
own beyond the API it is documenting. It is served from <base>/api/ and
|
|
12
|
+
own beyond the API it is documenting. It is served from <base>/api-console/ and
|
|
13
13
|
every request it makes is the same request an application makes, so the
|
|
14
14
|
service worker answers it and the answer is the mock's real answer.
|
|
15
15
|
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
*/
|
|
38
38
|
importScripts('host/runtime-core.js')
|
|
39
39
|
importScripts('host/api-browser.js')
|
|
40
|
+
importScripts('host/lint-browser.js')
|
|
40
41
|
|
|
41
42
|
const R = self.SASjsMockRuntime
|
|
42
43
|
|
|
@@ -967,19 +968,18 @@ const LINT_CONFIG_FILE = '.sasjslint'
|
|
|
967
968
|
* The linter, bundled at build time from the installed @sasjs/api/code and
|
|
968
969
|
* @sasjs/lint (see mockSite.ts).
|
|
969
970
|
*
|
|
970
|
-
* Imported
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
*
|
|
974
|
-
*
|
|
975
|
-
*
|
|
971
|
+
* Imported with the rest of the host, not on the first code route: a service
|
|
972
|
+
* worker may only `importScripts` while its own script is being evaluated, so a
|
|
973
|
+
* call made from a request handler fails with "The script at ... failed to load"
|
|
974
|
+
* - the linter would be missing exactly where it is needed. What stays lazy is the
|
|
975
|
+
* service built from it: `createCodeService` is cheap, and only a code route
|
|
976
|
+
* calls it.
|
|
976
977
|
*/
|
|
977
978
|
let codeService
|
|
978
979
|
let linter
|
|
979
980
|
|
|
980
981
|
const code = () => {
|
|
981
982
|
if (codeService) return codeService
|
|
982
|
-
importScripts('host/lint-browser.js')
|
|
983
983
|
linter = self.SASjsCode
|
|
984
984
|
codeService = linter.createCodeService({ lintConfig })
|
|
985
985
|
return codeService
|
|
@@ -1732,22 +1732,6 @@ const handle = async (request, url) => {
|
|
|
1732
1732
|
return response
|
|
1733
1733
|
}
|
|
1734
1734
|
|
|
1735
|
-
// The console's own files under `/api/`, replaced at build time from the list the
|
|
1736
|
-
// builder writes the folder from, so the two cannot drift apart.
|
|
1737
|
-
const API_CONSOLE_FILES = [
|
|
1738
|
-
'index.html',
|
|
1739
|
-
'console.js',
|
|
1740
|
-
'console.css',
|
|
1741
|
-
'operations.json'
|
|
1742
|
-
]
|
|
1743
|
-
|
|
1744
|
-
const CONSOLE_FILE = new RegExp(
|
|
1745
|
-
'^/api/(' +
|
|
1746
|
-
API_CONSOLE_FILES.map((name) => name.replace(/\./g, '\\.')).join('|') +
|
|
1747
|
-
')$',
|
|
1748
|
-
'i'
|
|
1749
|
-
)
|
|
1750
|
-
|
|
1751
1735
|
self.addEventListener('fetch', (event) => {
|
|
1752
1736
|
const url = new URL(event.request.url)
|
|
1753
1737
|
if (url.origin !== self.location.origin) return
|
|
@@ -1780,20 +1764,13 @@ self.addEventListener('fetch', (event) => {
|
|
|
1780
1764
|
// listing is the server's web route, and an interface that lists apps asks for
|
|
1781
1765
|
// it on every visit.
|
|
1782
1766
|
//
|
|
1783
|
-
// `/api` and `/api/` on their own are the
|
|
1784
|
-
//
|
|
1785
|
-
//
|
|
1786
|
-
// spelling, which routeRequest
|
|
1767
|
+
// `/api` and `/api/` on their own are left to the static host: a site's own
|
|
1768
|
+
// application can answer that route (this framework's server interface does),
|
|
1769
|
+
// and the worker has no business putting an API answer in front of it. Every
|
|
1770
|
+
// other `/api/...` is an API call on the short spelling, which routeRequest
|
|
1771
|
+
// canonicalises to `/SASjsApi`.
|
|
1787
1772
|
if (/^\/api\/?$/i.test(path)) return
|
|
1788
1773
|
|
|
1789
|
-
// And the console's own files, which live under that prefix: a path naming one
|
|
1790
|
-
// of them is the page's, not an API call, so the static host answers it. Named
|
|
1791
|
-
// one by one on purpose - an extension test would also pass through
|
|
1792
|
-
// `/api/admin/logs/foo.log`, which is a real call on the short spelling. The
|
|
1793
|
-
// list is replaced at build time with the one the builder writes the folder
|
|
1794
|
-
// from, so a file added there cannot arrive here as an API path.
|
|
1795
|
-
if (CONSOLE_FILE.test(path)) return
|
|
1796
|
-
|
|
1797
1774
|
if (!/^\/(SASjsApi|SASLogon|AppStream|api|app)(\/|$)/i.test(path)) return
|
|
1798
1775
|
event.respondWith(handle(event.request, url))
|
|
1799
1776
|
})
|
|
@@ -14,7 +14,7 @@ export interface MockSiteResult {
|
|
|
14
14
|
* large folder or file. Zero when the site ships no snapshot.
|
|
15
15
|
*/
|
|
16
16
|
snapshotShards: number;
|
|
17
|
-
/** True when the site carries the API console at <base>/api/. */
|
|
17
|
+
/** True when the site carries the API console at <base>/api-console/. */
|
|
18
18
|
apiConsole: boolean;
|
|
19
19
|
/** True when a recorded SAS session was shipped, for .sas runs to answer. */
|
|
20
20
|
sasTranscript: boolean;
|
|
@@ -1008,7 +1008,15 @@ var patchIndexHtml = function (html, version, basePath, outputFolder) {
|
|
|
1008
1008
|
}
|
|
1009
1009
|
return { patched: patched, warnings: warnings };
|
|
1010
1010
|
};
|
|
1011
|
-
/**
|
|
1011
|
+
/**
|
|
1012
|
+
* The folder the console lives in, under the site root.
|
|
1013
|
+
*
|
|
1014
|
+
* Not `api`: an application can answer `/api` itself - this framework's own server
|
|
1015
|
+
* interface does - and a page there would collide with its route, so a site
|
|
1016
|
+
* refresh on that screen would land on the console instead of the application.
|
|
1017
|
+
*/
|
|
1018
|
+
var API_CONSOLE_DIR = 'api-console';
|
|
1019
|
+
/** The files the console consists of, under `<output>/api-console`. */
|
|
1012
1020
|
var API_CONSOLE_FILES = [
|
|
1013
1021
|
'index.html',
|
|
1014
1022
|
'console.js',
|
|
@@ -1016,7 +1024,7 @@ var API_CONSOLE_FILES = [
|
|
|
1016
1024
|
'operations.json'
|
|
1017
1025
|
];
|
|
1018
1026
|
/**
|
|
1019
|
-
* The API console: a page at `<base>/api/` that lists every operation the API
|
|
1027
|
+
* The API console: a page at `<base>/api-console/` that lists every operation the API
|
|
1020
1028
|
* document declares and sends them against this host.
|
|
1021
1029
|
*
|
|
1022
1030
|
* The route table is the same one the worker answers from (`ROUTES` from
|
|
@@ -1028,8 +1036,8 @@ var API_CONSOLE_FILES = [
|
|
|
1028
1036
|
* a visit costs the demo nothing until someone types the URL.
|
|
1029
1037
|
*/
|
|
1030
1038
|
var writeApiConsole = function (outputFolder, version) {
|
|
1031
|
-
var folder = path_1.default.join(outputFolder,
|
|
1032
|
-
// `<base>/api/` is this build's, not the project's: a site that ships its own
|
|
1039
|
+
var folder = path_1.default.join(outputFolder, API_CONSOLE_DIR);
|
|
1040
|
+
// `<base>/api-console/` is this build's, not the project's: a site that ships its own
|
|
1033
1041
|
// files there would have them replaced without a word, so it keeps them and
|
|
1034
1042
|
// the console is left out
|
|
1035
1043
|
var taken = API_CONSOLE_FILES.filter(function (file) {
|
|
@@ -1040,7 +1048,7 @@ var writeApiConsole = function (outputFolder, version) {
|
|
|
1040
1048
|
written: false,
|
|
1041
1049
|
operations: 0,
|
|
1042
1050
|
warning: "The site already carries ".concat(taken
|
|
1043
|
-
.map(function (file) { return '
|
|
1051
|
+
.map(function (file) { return API_CONSOLE_DIR + '/' + file; })
|
|
1044
1052
|
.join(', '), ", so the API console was not written. ") +
|
|
1045
1053
|
'Move those files, or set streamConfig.mockApi to false to silence this.'
|
|
1046
1054
|
};
|
|
@@ -1260,10 +1268,7 @@ function buildMockSite(target) {
|
|
|
1260
1268
|
.readFileSync(swPath, 'utf8')
|
|
1261
1269
|
.replace("importScripts('host/runtime-core.js')", "importScripts('host/runtime-core.js?v=".concat(hostVersion, "')"))
|
|
1262
1270
|
.replace("importScripts('host/api-browser.js')", "importScripts('host/api-browser.js?v=".concat(hostVersion, "')"))
|
|
1263
|
-
.replace("importScripts('host/lint-browser.js')", "importScripts('host/lint-browser.js?v=".concat(hostVersion, "')"))
|
|
1264
|
-
// the console's file list, from the one place that writes the folder: a
|
|
1265
|
-
// second copy here would let a new file be served as an API path
|
|
1266
|
-
.replace(/const API_CONSOLE_FILES = \[[^\]]*\]/, "const API_CONSOLE_FILES = ".concat(JSON.stringify(API_CONSOLE_FILES))));
|
|
1271
|
+
.replace("importScripts('host/lint-browser.js')", "importScripts('host/lint-browser.js?v=".concat(hostVersion, "')")));
|
|
1267
1272
|
snapshotSource = path_1.default.join(buildFolder, 'mock-snapshot.json');
|
|
1268
1273
|
hasSnapshot = fs_1.default.existsSync(snapshotSource);
|
|
1269
1274
|
snapshotShards = 0;
|
|
@@ -675,7 +675,7 @@ describe('the API console', function () {
|
|
|
675
675
|
case 0:
|
|
676
676
|
jest.resetModules();
|
|
677
677
|
document.body.innerHTML = bodyOf(page());
|
|
678
|
-
history.replaceState({}, '', '/api/?route=' + encodeURIComponent('GET /SASjsApi/user'));
|
|
678
|
+
history.replaceState({}, '', '/api-console/?route=' + encodeURIComponent('GET /SASjsApi/user'));
|
|
679
679
|
global.fetch = jest.fn(function (url) { return __awaiter(void 0, void 0, void 0, function () {
|
|
680
680
|
return __generator(this, function (_a) {
|
|
681
681
|
return [2 /*return*/, String(url).includes('operations.json')
|
|
@@ -699,7 +699,7 @@ describe('the API console', function () {
|
|
|
699
699
|
return [3 /*break*/, 1];
|
|
700
700
|
case 4:
|
|
701
701
|
expect(document.getElementById('title').textContent).toBe('GET /SASjsApi/user');
|
|
702
|
-
history.replaceState({}, '', '/api/');
|
|
702
|
+
history.replaceState({}, '', '/api-console/');
|
|
703
703
|
return [2 /*return*/];
|
|
704
704
|
}
|
|
705
705
|
});
|
|
@@ -1093,7 +1093,7 @@ describe('the API console', function () {
|
|
|
1093
1093
|
// jsdom does not navigate, and the redirect is the last thing the page does:
|
|
1094
1094
|
// what matters here is that the drive was reset before it
|
|
1095
1095
|
return [4 /*yield*/, loadConsole(function () {
|
|
1096
|
-
history.replaceState({}, '', '/api/?reset=1');
|
|
1096
|
+
history.replaceState({}, '', '/api-console/?reset=1');
|
|
1097
1097
|
window.SASjsMock = { reset: reset };
|
|
1098
1098
|
})];
|
|
1099
1099
|
case 1:
|
|
@@ -1105,7 +1105,7 @@ describe('the API console', function () {
|
|
|
1105
1105
|
_a.sent();
|
|
1106
1106
|
expect(reset).toHaveBeenCalled();
|
|
1107
1107
|
expect(sessionStorage.length).toBe(0);
|
|
1108
|
-
history.replaceState({}, '', '/api/');
|
|
1108
|
+
history.replaceState({}, '', '/api-console/');
|
|
1109
1109
|
return [2 /*return*/];
|
|
1110
1110
|
}
|
|
1111
1111
|
});
|
|
@@ -1125,7 +1125,7 @@ describe('the API console', function () {
|
|
|
1125
1125
|
return [4 /*yield*/, loadConsole(function () {
|
|
1126
1126
|
// no host bridge on this page at all
|
|
1127
1127
|
delete window.SASjsMock;
|
|
1128
|
-
history.replaceState({}, '', '/api/?reset=1');
|
|
1128
|
+
history.replaceState({}, '', '/api-console/?reset=1');
|
|
1129
1129
|
})];
|
|
1130
1130
|
case 1:
|
|
1131
1131
|
_a.sent();
|
|
@@ -1136,7 +1136,7 @@ describe('the API console', function () {
|
|
|
1136
1136
|
_a.sent();
|
|
1137
1137
|
expect(global.indexedDB.deleteDatabase).toHaveBeenCalledWith('sasjs-mock-host');
|
|
1138
1138
|
expect(document.getElementById('routes').textContent).toContain('still holds the drive');
|
|
1139
|
-
history.replaceState({}, '', '/api/');
|
|
1139
|
+
history.replaceState({}, '', '/api-console/');
|
|
1140
1140
|
delete global.indexedDB;
|
|
1141
1141
|
return [2 /*return*/];
|
|
1142
1142
|
}
|
|
@@ -1152,7 +1152,7 @@ describe('the API console', function () {
|
|
|
1152
1152
|
}); }); });
|
|
1153
1153
|
sessionStorage.setItem('mock-console-token', 'kept');
|
|
1154
1154
|
return [4 /*yield*/, loadConsole(function () {
|
|
1155
|
-
history.replaceState({}, '', '/api/?reset=0');
|
|
1155
|
+
history.replaceState({}, '', '/api-console/?reset=0');
|
|
1156
1156
|
window.SASjsMock = { reset: reset };
|
|
1157
1157
|
})];
|
|
1158
1158
|
case 1:
|
|
@@ -1162,7 +1162,7 @@ describe('the API console', function () {
|
|
|
1162
1162
|
_a.sent();
|
|
1163
1163
|
expect(reset).not.toHaveBeenCalled();
|
|
1164
1164
|
expect(sessionStorage.getItem('mock-console-token')).toBe('kept');
|
|
1165
|
-
history.replaceState({}, '', '/api/');
|
|
1165
|
+
history.replaceState({}, '', '/api-console/');
|
|
1166
1166
|
return [2 /*return*/];
|
|
1167
1167
|
}
|
|
1168
1168
|
});
|
|
@@ -1331,6 +1331,72 @@ describe('the API console', function () {
|
|
|
1331
1331
|
}
|
|
1332
1332
|
});
|
|
1333
1333
|
}); });
|
|
1334
|
+
it('says so, rather than reloading again, when the worker never claims the page', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1335
|
+
var error, host, i;
|
|
1336
|
+
return __generator(this, function (_a) {
|
|
1337
|
+
switch (_a.label) {
|
|
1338
|
+
case 0:
|
|
1339
|
+
error = jest.spyOn(console, 'error').mockImplementation(function () { });
|
|
1340
|
+
host = withServiceWorker();
|
|
1341
|
+
sessionStorage.setItem('mock-console-reloaded', '1');
|
|
1342
|
+
return [4 /*yield*/, loadConsole()];
|
|
1343
|
+
case 1:
|
|
1344
|
+
_a.sent();
|
|
1345
|
+
i = 0;
|
|
1346
|
+
_a.label = 2;
|
|
1347
|
+
case 2:
|
|
1348
|
+
if (!(i < 200)) return [3 /*break*/, 5];
|
|
1349
|
+
if (error.mock.calls.some(function (call) { return /take control/.test(String(call[0])); }))
|
|
1350
|
+
return [3 /*break*/, 5];
|
|
1351
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 25); })];
|
|
1352
|
+
case 3:
|
|
1353
|
+
_a.sent();
|
|
1354
|
+
_a.label = 4;
|
|
1355
|
+
case 4:
|
|
1356
|
+
i++;
|
|
1357
|
+
return [3 /*break*/, 2];
|
|
1358
|
+
case 5:
|
|
1359
|
+
expect(error.mock.calls.some(function (call) { return /take control/.test(String(call[0])); })).toBe(true);
|
|
1360
|
+
expect(host.sw.register).toHaveBeenCalled();
|
|
1361
|
+
error.mockRestore();
|
|
1362
|
+
return [2 /*return*/];
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
}); });
|
|
1366
|
+
it('returns to its own page after a reset, wherever that page is', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1367
|
+
var deleted, page;
|
|
1368
|
+
return __generator(this, function (_a) {
|
|
1369
|
+
switch (_a.label) {
|
|
1370
|
+
case 0:
|
|
1371
|
+
deleted = [];
|
|
1372
|
+
global.indexedDB = {
|
|
1373
|
+
deleteDatabase: function (name) {
|
|
1374
|
+
deleted.push(name);
|
|
1375
|
+
var request = {};
|
|
1376
|
+
setTimeout(function () { return request.onsuccess && request.onsuccess(); }, 5);
|
|
1377
|
+
return request;
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
return [4 /*yield*/, loadConsole(function () {
|
|
1381
|
+
delete window.SASjsMock;
|
|
1382
|
+
history.replaceState({}, '', '/demo/api-console/?reset=1&route=GET%20%2FSASjsApi%2Fuser');
|
|
1383
|
+
})];
|
|
1384
|
+
case 1:
|
|
1385
|
+
_a.sent();
|
|
1386
|
+
return [4 /*yield*/, settle(function () { return deleted.length > 0; })];
|
|
1387
|
+
case 2:
|
|
1388
|
+
_a.sent();
|
|
1389
|
+
page = window.SASjsApiConsole;
|
|
1390
|
+
expect(page).toBeDefined();
|
|
1391
|
+
// the folder is the page's, the flag goes, and the route the visitor was on
|
|
1392
|
+
// stays: a reset should not cost them the deep link
|
|
1393
|
+
expect(page.resetTarget()).toBe('/demo/api-console/?route=GET+%2FSASjsApi%2Fuser');
|
|
1394
|
+
delete global.indexedDB;
|
|
1395
|
+
history.replaceState({}, '', '/api-console/');
|
|
1396
|
+
return [2 /*return*/];
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
}); });
|
|
1334
1400
|
it('drops the token when the visitor goes back to the demo administrator', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1335
1401
|
var calls, back;
|
|
1336
1402
|
return __generator(this, function (_a) {
|
|
@@ -66,7 +66,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
66
66
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
67
67
|
/**
|
|
68
68
|
* @jest-environment jsdom
|
|
69
|
-
* @jest-environment-options {"url": "http://localhost/demo/api/"}
|
|
69
|
+
* @jest-environment-options {"url": "http://localhost/demo/api-console/"}
|
|
70
70
|
*/
|
|
71
71
|
var fs_1 = __importDefault(require("fs"));
|
|
72
72
|
var path_1 = __importDefault(require("path"));
|
|
@@ -74,11 +74,10 @@ var path_1 = __importDefault(require("path"));
|
|
|
74
74
|
* The console under a base path.
|
|
75
75
|
*
|
|
76
76
|
* `mockWebBasePath` is a supported option (a site published at `/demo`), and the
|
|
77
|
-
* console
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* still renders, and every call goes to a path the host does not answer.
|
|
77
|
+
* console works out its root from the worker it registered - `<base>/sw.js` - with
|
|
78
|
+
* its own URL as the fallback. This file runs the page at `/demo/api-console/`,
|
|
79
|
+
* where a wrong derivation is silent: the page still renders, and every call goes
|
|
80
|
+
* to a path the host does not answer.
|
|
82
81
|
*/
|
|
83
82
|
var assets = path_1.default.join(__dirname, '..', 'assets');
|
|
84
83
|
var page = function () { return fs_1.default.readFileSync(path_1.default.join(assets, 'api-index.html'), 'utf8'); };
|
|
@@ -136,6 +135,119 @@ describe('the API console under a base path', function () {
|
|
|
136
135
|
afterEach(function () {
|
|
137
136
|
delete navigator.serviceWorker;
|
|
138
137
|
});
|
|
138
|
+
it('takes the root from the worker that covers it, not from its own folder', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
139
|
+
var realCreate, sw, asked;
|
|
140
|
+
return __generator(this, function (_a) {
|
|
141
|
+
switch (_a.label) {
|
|
142
|
+
case 0:
|
|
143
|
+
// the shapes where the two disagree are the only ones that prove the worker
|
|
144
|
+
// is read at all: the site's worker at `/demo/sw.js` controlling a console
|
|
145
|
+
// two folders down still names `/demo/`, which the folder alone cannot
|
|
146
|
+
document.body.innerHTML = bodyOf(page());
|
|
147
|
+
document.head.innerHTML =
|
|
148
|
+
'<meta name="sasjs-mock-version" content="3.0.0" />';
|
|
149
|
+
global.fetch = jest.fn(function (url) { return __awaiter(void 0, void 0, void 0, function () {
|
|
150
|
+
return __generator(this, function (_a) {
|
|
151
|
+
return [2 /*return*/, String(url).includes('operations.json')
|
|
152
|
+
? jsonResponse(operations)
|
|
153
|
+
: jsonResponse({ format: 1, users: [] })];
|
|
154
|
+
});
|
|
155
|
+
}); });
|
|
156
|
+
realCreate = document.createElement.bind(document);
|
|
157
|
+
jest.spyOn(document, 'createElement').mockImplementation(function (tag) {
|
|
158
|
+
var node = realCreate(tag);
|
|
159
|
+
if (String(tag).toLowerCase() === 'script')
|
|
160
|
+
setTimeout(function () { return node.dispatchEvent(new Event('load')); }, 0);
|
|
161
|
+
return node;
|
|
162
|
+
});
|
|
163
|
+
sw = {
|
|
164
|
+
controller: { scriptURL: 'http://localhost/demo/sw.js' },
|
|
165
|
+
register: jest.fn(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
166
|
+
return [2 /*return*/, ({})];
|
|
167
|
+
}); }); }),
|
|
168
|
+
addEventListener: jest.fn()
|
|
169
|
+
};
|
|
170
|
+
Object.defineProperty(navigator, 'serviceWorker', {
|
|
171
|
+
value: sw,
|
|
172
|
+
configurable: true
|
|
173
|
+
});
|
|
174
|
+
history.replaceState({}, '', '/demo/tools/api-console/');
|
|
175
|
+
jest.resetModules();
|
|
176
|
+
require('../assets/api-console.js');
|
|
177
|
+
return [4 /*yield*/, settle(function () {
|
|
178
|
+
return global.fetch.mock.calls.some(function (call) {
|
|
179
|
+
return String(call[0]).includes('identity.json');
|
|
180
|
+
});
|
|
181
|
+
})];
|
|
182
|
+
case 1:
|
|
183
|
+
_a.sent();
|
|
184
|
+
asked = global.fetch.mock.calls.map(function (call) {
|
|
185
|
+
return String(call[0]);
|
|
186
|
+
});
|
|
187
|
+
// the site's root, not the folder two levels up from the page
|
|
188
|
+
expect(asked).toContain('/demo/host/identity.json');
|
|
189
|
+
expect(asked.some(function (url) { return url.startsWith('/demo/tools/'); })).toBe(false);
|
|
190
|
+
history.replaceState({}, '', '/demo/tools/api-console/');
|
|
191
|
+
return [2 /*return*/];
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}); });
|
|
195
|
+
it('ignores a worker that belongs to another site', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
196
|
+
var realCreate, sw, asked;
|
|
197
|
+
return __generator(this, function (_a) {
|
|
198
|
+
switch (_a.label) {
|
|
199
|
+
case 0:
|
|
200
|
+
// a page can be controlled by some other site's worker; its `sw.js` names a
|
|
201
|
+
// root that has nothing to do with this console, so the page's own folder has
|
|
202
|
+
// to win
|
|
203
|
+
document.body.innerHTML = bodyOf(page());
|
|
204
|
+
document.head.innerHTML =
|
|
205
|
+
'<meta name="sasjs-mock-version" content="3.0.0" />';
|
|
206
|
+
global.fetch = jest.fn(function (url) { return __awaiter(void 0, void 0, void 0, function () {
|
|
207
|
+
return __generator(this, function (_a) {
|
|
208
|
+
return [2 /*return*/, String(url).includes('operations.json')
|
|
209
|
+
? jsonResponse(operations)
|
|
210
|
+
: jsonResponse({ format: 1, users: [] })];
|
|
211
|
+
});
|
|
212
|
+
}); });
|
|
213
|
+
realCreate = document.createElement.bind(document);
|
|
214
|
+
jest.spyOn(document, 'createElement').mockImplementation(function (tag) {
|
|
215
|
+
var node = realCreate(tag);
|
|
216
|
+
if (String(tag).toLowerCase() === 'script')
|
|
217
|
+
setTimeout(function () { return node.dispatchEvent(new Event('load')); }, 0);
|
|
218
|
+
return node;
|
|
219
|
+
});
|
|
220
|
+
sw = {
|
|
221
|
+
controller: { scriptURL: 'http://localhost/other/sw.js' },
|
|
222
|
+
register: jest.fn(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
223
|
+
return [2 /*return*/, ({})];
|
|
224
|
+
}); }); }),
|
|
225
|
+
addEventListener: jest.fn()
|
|
226
|
+
};
|
|
227
|
+
Object.defineProperty(navigator, 'serviceWorker', {
|
|
228
|
+
value: sw,
|
|
229
|
+
configurable: true
|
|
230
|
+
});
|
|
231
|
+
history.replaceState({}, '', '/demo/api-console/');
|
|
232
|
+
jest.resetModules();
|
|
233
|
+
require('../assets/api-console.js');
|
|
234
|
+
return [4 /*yield*/, settle(function () {
|
|
235
|
+
return global.fetch.mock.calls.some(function (call) {
|
|
236
|
+
return String(call[0]).includes('identity.json');
|
|
237
|
+
});
|
|
238
|
+
})];
|
|
239
|
+
case 1:
|
|
240
|
+
_a.sent();
|
|
241
|
+
asked = global.fetch.mock.calls.map(function (call) {
|
|
242
|
+
return String(call[0]);
|
|
243
|
+
});
|
|
244
|
+
expect(asked).toContain('/demo/host/identity.json');
|
|
245
|
+
expect(asked.some(function (url) { return url.startsWith('/other/'); })).toBe(false);
|
|
246
|
+
history.replaceState({}, '', '/demo/api-console/');
|
|
247
|
+
return [2 /*return*/];
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}); });
|
|
139
251
|
it('finds the folder from a URL without its trailing slash', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
140
252
|
var realCreate, sw, asked;
|
|
141
253
|
return __generator(this, function (_a) {
|
|
@@ -143,8 +255,8 @@ describe('the API console under a base path', function () {
|
|
|
143
255
|
case 0:
|
|
144
256
|
// `/demo/api` is the shape a person types, and the page used to read that as
|
|
145
257
|
// the origin root: every call went to /SASjsApi/... and the worker to /sw.js
|
|
146
|
-
history.replaceState({}, '', '/demo/api');
|
|
147
|
-
expect(location.pathname).toBe('/demo/api');
|
|
258
|
+
history.replaceState({}, '', '/demo/api-console');
|
|
259
|
+
expect(location.pathname).toBe('/demo/api-console');
|
|
148
260
|
document.body.innerHTML = bodyOf(page());
|
|
149
261
|
document.head.innerHTML =
|
|
150
262
|
'<meta name="sasjs-mock-version" content="2.0.0" />';
|
|
@@ -187,7 +299,7 @@ describe('the API console under a base path', function () {
|
|
|
187
299
|
return String(call[0]);
|
|
188
300
|
});
|
|
189
301
|
expect(asked).toContain('/demo/host/identity.json');
|
|
190
|
-
history.replaceState({}, '', '/demo/api/');
|
|
302
|
+
history.replaceState({}, '', '/demo/api-console/');
|
|
191
303
|
return [2 /*return*/];
|
|
192
304
|
}
|
|
193
305
|
});
|
|
@@ -1051,14 +1051,14 @@ describe('browser-hosted build', function () {
|
|
|
1051
1051
|
* the host drift.
|
|
1052
1052
|
*/
|
|
1053
1053
|
it('ships an API console built from the route table', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1054
|
-
var result, folder, shipped, sw,
|
|
1054
|
+
var result, folder, shipped, sw, operations, createUser, html, version, appHtml, loader;
|
|
1055
1055
|
return __generator(this, function (_a) {
|
|
1056
1056
|
switch (_a.label) {
|
|
1057
1057
|
case 0: return [4 /*yield*/, (0, mockSite_1.buildMockSite)(target())];
|
|
1058
1058
|
case 1:
|
|
1059
1059
|
result = _a.sent();
|
|
1060
1060
|
expect(result.apiConsole).toBe(true);
|
|
1061
|
-
folder = path_1.default.join(out(), 'api');
|
|
1061
|
+
folder = path_1.default.join(out(), 'api-console');
|
|
1062
1062
|
shipped = fs_1.default.readdirSync(folder).sort();
|
|
1063
1063
|
// every file the folder holds, and the worker's passthrough list is that same
|
|
1064
1064
|
// list: a second copy in sw.js would let a new file be answered as an API path
|
|
@@ -1069,9 +1069,7 @@ describe('browser-hosted build', function () {
|
|
|
1069
1069
|
'operations.json'
|
|
1070
1070
|
]);
|
|
1071
1071
|
sw = fs_1.default.readFileSync(path_1.default.join(out(), 'sw.js'), 'utf8');
|
|
1072
|
-
|
|
1073
|
-
expect(listed).not.toBeNull();
|
|
1074
|
-
expect(JSON.parse(listed[1]).sort()).toEqual(shipped);
|
|
1072
|
+
expect(sw).not.toContain('API_CONSOLE_FILES');
|
|
1075
1073
|
operations = JSON.parse(fs_1.default.readFileSync(path_1.default.join(folder, 'operations.json'), 'utf8'));
|
|
1076
1074
|
expect(operations.length).toBeGreaterThan(20);
|
|
1077
1075
|
createUser = operations.find(function (operation) { return operation.id === 'POST /SASjsApi/user'; });
|
|
@@ -1091,12 +1089,12 @@ describe('browser-hosted build', function () {
|
|
|
1091
1089
|
}
|
|
1092
1090
|
});
|
|
1093
1091
|
}); });
|
|
1094
|
-
it('leaves the console out when the site ships its own
|
|
1092
|
+
it('leaves the console out when the site ships its own console folder, and says so', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1095
1093
|
var own, result;
|
|
1096
1094
|
return __generator(this, function (_a) {
|
|
1097
1095
|
switch (_a.label) {
|
|
1098
1096
|
case 0:
|
|
1099
|
-
own = path_1.default.join(root, 'client', 'dist', 'api');
|
|
1097
|
+
own = path_1.default.join(root, 'client', 'dist', 'api-console');
|
|
1100
1098
|
fs_1.default.mkdirSync(own, { recursive: true });
|
|
1101
1099
|
fs_1.default.writeFileSync(path_1.default.join(own, 'index.html'), '<p>ours</p>');
|
|
1102
1100
|
return [4 /*yield*/, (0, mockSite_1.buildMockSite)(target())
|
|
@@ -1108,9 +1106,9 @@ describe('browser-hosted build', function () {
|
|
|
1108
1106
|
// the project's file is not replaced, and the loss is reported rather than
|
|
1109
1107
|
// silent: the build has no way to know which of the two the site wants
|
|
1110
1108
|
expect(result.apiConsole).toBe(false);
|
|
1111
|
-
expect(fs_1.default.readFileSync(path_1.default.join(out(), 'api', 'index.html'), 'utf8')).toBe('<p>ours</p>');
|
|
1112
|
-
expect(fs_1.default.existsSync(path_1.default.join(out(), 'api', 'console.js'))).toBe(false);
|
|
1113
|
-
expect(process.logger.warn).toHaveBeenCalledWith(expect.stringContaining('api/index.html'));
|
|
1109
|
+
expect(fs_1.default.readFileSync(path_1.default.join(out(), 'api-console', 'index.html'), 'utf8')).toBe('<p>ours</p>');
|
|
1110
|
+
expect(fs_1.default.existsSync(path_1.default.join(out(), 'api-console', 'console.js'))).toBe(false);
|
|
1111
|
+
expect(process.logger.warn).toHaveBeenCalledWith(expect.stringContaining('api-console/index.html'));
|
|
1114
1112
|
return [2 /*return*/];
|
|
1115
1113
|
}
|
|
1116
1114
|
});
|
|
@@ -1123,7 +1121,7 @@ describe('browser-hosted build', function () {
|
|
|
1123
1121
|
case 1:
|
|
1124
1122
|
result = _a.sent();
|
|
1125
1123
|
expect(result.apiConsole).toBe(false);
|
|
1126
|
-
expect(fs_1.default.existsSync(path_1.default.join(out(), 'api'))).toBe(false);
|
|
1124
|
+
expect(fs_1.default.existsSync(path_1.default.join(out(), 'api-console'))).toBe(false);
|
|
1127
1125
|
return [2 /*return*/];
|
|
1128
1126
|
}
|
|
1129
1127
|
});
|
|
@@ -86,7 +86,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
86
86
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
87
87
|
};
|
|
88
88
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89
|
-
var fs_1 = __importDefault(require("fs"));
|
|
90
89
|
var path_1 = __importDefault(require("path"));
|
|
91
90
|
var api_1 = require("@sasjs/api");
|
|
92
91
|
var os_1 = __importDefault(require("os"));
|
|
@@ -1443,25 +1442,27 @@ describe('service worker host', function () {
|
|
|
1443
1442
|
});
|
|
1444
1443
|
}); });
|
|
1445
1444
|
/**
|
|
1446
|
-
* The linter is
|
|
1445
|
+
* The linter is imported with the host, because it has to be.
|
|
1447
1446
|
*
|
|
1448
|
-
*
|
|
1449
|
-
*
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1447
|
+
* A service worker may only `importScripts` while its own script is being
|
|
1448
|
+
* evaluated: a call made from a request handler fails with "The script at ...
|
|
1449
|
+
* failed to load", which is what a lazy import gave a real browser - the two
|
|
1450
|
+
* code routes answered 500. So the import is at boot, and the assertion is
|
|
1451
|
+
* that a code route answers *without* importing anything at request time.
|
|
1452
1452
|
*/
|
|
1453
|
-
it('
|
|
1454
|
-
var worker, response;
|
|
1453
|
+
it('imports the linter at boot and answers a code route without importing', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1454
|
+
var worker, before, response;
|
|
1455
1455
|
return __generator(this, function (_a) {
|
|
1456
1456
|
switch (_a.label) {
|
|
1457
1457
|
case 0:
|
|
1458
1458
|
worker = seeded();
|
|
1459
|
-
expect(worker.imported.some(function (path) { return path.includes('lint-browser'); })).toBe(
|
|
1459
|
+
expect(worker.imported.some(function (path) { return path.includes('lint-browser'); })).toBe(true);
|
|
1460
|
+
before = worker.imported.length;
|
|
1460
1461
|
return [4 /*yield*/, worker.dispatch('/SASjsApi/code/lint', runRequest({ code: 'data a;\nrun;\n' }))];
|
|
1461
1462
|
case 1:
|
|
1462
1463
|
response = _a.sent();
|
|
1463
1464
|
expect(response.status).toBe(200);
|
|
1464
|
-
expect(worker.imported.
|
|
1465
|
+
expect(worker.imported.length).toBe(before);
|
|
1465
1466
|
return [2 /*return*/];
|
|
1466
1467
|
}
|
|
1467
1468
|
});
|
|
@@ -2566,46 +2567,25 @@ describe('service worker host', function () {
|
|
|
2566
2567
|
* The console's own files are the page, not an API call.
|
|
2567
2568
|
*
|
|
2568
2569
|
* `/api/...` is the API's short spelling, so the worker answers it - except for
|
|
2569
|
-
*
|
|
2570
|
-
* that wrong answers the page's own script with an API 404, and the console
|
|
2571
|
-
* renders nothing.
|
|
2570
|
+
* `/api` itself, which a site's own application may answer.
|
|
2572
2571
|
*/
|
|
2573
|
-
it('leaves
|
|
2574
|
-
var worker,
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
switch (_b.label) {
|
|
2572
|
+
it('leaves /api to the site, and answers the short spelling below it', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2573
|
+
var worker, response;
|
|
2574
|
+
return __generator(this, function (_a) {
|
|
2575
|
+
switch (_a.label) {
|
|
2578
2576
|
case 0:
|
|
2579
2577
|
worker = seeded();
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
expect(
|
|
2583
|
-
files = listed[1]
|
|
2584
|
-
.split(',')
|
|
2585
|
-
.map(function (entry) { return entry.trim().replace(/^'|'$/g, ''); })
|
|
2586
|
-
.filter(Boolean);
|
|
2587
|
-
expect(files.length).toBeGreaterThan(3);
|
|
2578
|
+
// a site's own application can answer /api (the server interface does), so the
|
|
2579
|
+
// worker hands it on; `/api/<something>` is an API call on the short spelling
|
|
2580
|
+
expect(worker.dispatch('/api')).toBeUndefined();
|
|
2588
2581
|
expect(worker.dispatch('/api/')).toBeUndefined();
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
2596
|
-
finally {
|
|
2597
|
-
try {
|
|
2598
|
-
if (files_1_1 && !files_1_1.done && (_a = files_1.return)) _a.call(files_1);
|
|
2599
|
-
}
|
|
2600
|
-
finally { if (e_2) throw e_2.error; }
|
|
2601
|
-
}
|
|
2602
|
-
return [4 /*yield*/, worker.dispatch('/api/admin/logs/missing.log')];
|
|
2603
|
-
case 1:
|
|
2604
|
-
log = _b.sent();
|
|
2605
|
-
expect(log.status).not.toBeUndefined();
|
|
2582
|
+
// the console has a folder of its own, which is not an API prefix: the static
|
|
2583
|
+
// host serves it and the worker never sees it
|
|
2584
|
+
expect(worker.dispatch('/api-console/console.js')).toBeUndefined();
|
|
2585
|
+
expect(worker.dispatch('/api-console/operations.json')).toBeUndefined();
|
|
2606
2586
|
return [4 /*yield*/, worker.dispatch('/api/user')];
|
|
2607
|
-
case
|
|
2608
|
-
response =
|
|
2587
|
+
case 1:
|
|
2588
|
+
response = _a.sent();
|
|
2609
2589
|
expect(response.status).toBe(200);
|
|
2610
2590
|
return [2 /*return*/];
|
|
2611
2591
|
}
|
|
@@ -2630,8 +2610,8 @@ describe('service worker host', function () {
|
|
|
2630
2610
|
});
|
|
2631
2611
|
}); });
|
|
2632
2612
|
it('publishes, lists, serves and removes an app through the package surface', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2633
|
-
var dispatch, _a, _b, path_2, listing, _c,
|
|
2634
|
-
var
|
|
2613
|
+
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;
|
|
2614
|
+
var e_2, _q, e_3, _r, e_4, _s, e_5, _t;
|
|
2635
2615
|
return __generator(this, function (_u) {
|
|
2636
2616
|
switch (_u.label) {
|
|
2637
2617
|
case 0:
|
|
@@ -2658,14 +2638,14 @@ describe('service worker host', function () {
|
|
|
2658
2638
|
return [3 /*break*/, 2];
|
|
2659
2639
|
case 6: return [3 /*break*/, 9];
|
|
2660
2640
|
case 7:
|
|
2661
|
-
|
|
2662
|
-
|
|
2641
|
+
e_2_1 = _u.sent();
|
|
2642
|
+
e_2 = { error: e_2_1 };
|
|
2663
2643
|
return [3 /*break*/, 9];
|
|
2664
2644
|
case 8:
|
|
2665
2645
|
try {
|
|
2666
2646
|
if (_b && !_b.done && (_q = _a.return)) _q.call(_a);
|
|
2667
2647
|
}
|
|
2668
|
-
finally { if (
|
|
2648
|
+
finally { if (e_2) throw e_2.error; }
|
|
2669
2649
|
return [7 /*endfinally*/];
|
|
2670
2650
|
case 9:
|
|
2671
2651
|
_u.trys.push([9, 14, 15, 16]);
|
|
@@ -2685,14 +2665,14 @@ describe('service worker host', function () {
|
|
|
2685
2665
|
return [3 /*break*/, 10];
|
|
2686
2666
|
case 13: return [3 /*break*/, 16];
|
|
2687
2667
|
case 14:
|
|
2688
|
-
|
|
2689
|
-
|
|
2668
|
+
e_3_1 = _u.sent();
|
|
2669
|
+
e_3 = { error: e_3_1 };
|
|
2690
2670
|
return [3 /*break*/, 16];
|
|
2691
2671
|
case 15:
|
|
2692
2672
|
try {
|
|
2693
2673
|
if (_e && !_e.done && (_r = _d.return)) _r.call(_d);
|
|
2694
2674
|
}
|
|
2695
|
-
finally { if (
|
|
2675
|
+
finally { if (e_3) throw e_3.error; }
|
|
2696
2676
|
return [7 /*endfinally*/];
|
|
2697
2677
|
case 16: return [4 /*yield*/, dispatch('/AppStream')];
|
|
2698
2678
|
case 17:
|
|
@@ -2745,14 +2725,14 @@ describe('service worker host', function () {
|
|
|
2745
2725
|
return [3 /*break*/, 22];
|
|
2746
2726
|
case 26: return [3 /*break*/, 29];
|
|
2747
2727
|
case 27:
|
|
2748
|
-
|
|
2749
|
-
|
|
2728
|
+
e_4_1 = _u.sent();
|
|
2729
|
+
e_4 = { error: e_4_1 };
|
|
2750
2730
|
return [3 /*break*/, 29];
|
|
2751
2731
|
case 28:
|
|
2752
2732
|
try {
|
|
2753
2733
|
if (_g && !_g.done && (_s = _f.return)) _s.call(_f);
|
|
2754
2734
|
}
|
|
2755
|
-
finally { if (
|
|
2735
|
+
finally { if (e_4) throw e_4.error; }
|
|
2756
2736
|
return [7 /*endfinally*/];
|
|
2757
2737
|
case 29:
|
|
2758
2738
|
_u.trys.push([29, 35, 36, 37]);
|
|
@@ -2779,14 +2759,14 @@ describe('service worker host', function () {
|
|
|
2779
2759
|
return [3 /*break*/, 30];
|
|
2780
2760
|
case 34: return [3 /*break*/, 37];
|
|
2781
2761
|
case 35:
|
|
2782
|
-
|
|
2783
|
-
|
|
2762
|
+
e_5_1 = _u.sent();
|
|
2763
|
+
e_5 = { error: e_5_1 };
|
|
2784
2764
|
return [3 /*break*/, 37];
|
|
2785
2765
|
case 36:
|
|
2786
2766
|
try {
|
|
2787
2767
|
if (_k && !_k.done && (_t = _j.return)) _t.call(_j);
|
|
2788
2768
|
}
|
|
2789
|
-
finally { if (
|
|
2769
|
+
finally { if (e_5) throw e_5.error; }
|
|
2790
2770
|
return [7 /*endfinally*/];
|
|
2791
2771
|
case 37: return [4 /*yield*/, dispatch("/app/".concat(app.id))];
|
|
2792
2772
|
case 38:
|
|
@@ -3735,7 +3715,7 @@ describe('service worker host', function () {
|
|
|
3735
3715
|
}); });
|
|
3736
3716
|
});
|
|
3737
3717
|
describe('conformance corpus', function () {
|
|
3738
|
-
var
|
|
3718
|
+
var e_6, _a, e_7, _b;
|
|
3739
3719
|
/**
|
|
3740
3720
|
* The corpus is what "the same API" means: the cases @sasjs/api runs against
|
|
3741
3721
|
* its own services, and against a live sasjs/server, run here against this
|
|
@@ -3744,7 +3724,7 @@ describe('service worker host', function () {
|
|
|
3744
3724
|
* used to have - and it is a test rather than a review.
|
|
3745
3725
|
*/
|
|
3746
3726
|
var httpFor = function (testCase) {
|
|
3747
|
-
var
|
|
3727
|
+
var e_8, _a;
|
|
3748
3728
|
var _b;
|
|
3749
3729
|
var _c = __read(testCase.route.split(' '), 2), method = _c[0], routePath = _c[1];
|
|
3750
3730
|
var query = new URLSearchParams();
|
|
@@ -3755,12 +3735,12 @@ describe('service worker host', function () {
|
|
|
3755
3735
|
query.set(key, String(value));
|
|
3756
3736
|
}
|
|
3757
3737
|
}
|
|
3758
|
-
catch (
|
|
3738
|
+
catch (e_8_1) { e_8 = { error: e_8_1 }; }
|
|
3759
3739
|
finally {
|
|
3760
3740
|
try {
|
|
3761
3741
|
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
|
|
3762
3742
|
}
|
|
3763
|
-
finally { if (
|
|
3743
|
+
finally { if (e_8) throw e_8.error; }
|
|
3764
3744
|
}
|
|
3765
3745
|
var suffix = query.toString() ? "?".concat(query) : '';
|
|
3766
3746
|
var init = { method: method };
|
|
@@ -3791,8 +3771,8 @@ describe('service worker host', function () {
|
|
|
3791
3771
|
* does.
|
|
3792
3772
|
*/
|
|
3793
3773
|
var seedDrive = function (runtime, testCase) { return __awaiter(void 0, void 0, void 0, function () {
|
|
3794
|
-
var text, _a, _b, _c, file, contents, _d, _e, _f, file, contents,
|
|
3795
|
-
var
|
|
3774
|
+
var text, _a, _b, _c, file, contents, _d, _e, _f, file, contents, e_9_1;
|
|
3775
|
+
var e_10, _g, e_9, _h;
|
|
3796
3776
|
return __generator(this, function (_j) {
|
|
3797
3777
|
switch (_j.label) {
|
|
3798
3778
|
case 0:
|
|
@@ -3804,12 +3784,12 @@ describe('service worker host', function () {
|
|
|
3804
3784
|
text["/sasjs/drive/files".concat(file)] = contents;
|
|
3805
3785
|
}
|
|
3806
3786
|
}
|
|
3807
|
-
catch (
|
|
3787
|
+
catch (e_10_1) { e_10 = { error: e_10_1 }; }
|
|
3808
3788
|
finally {
|
|
3809
3789
|
try {
|
|
3810
3790
|
if (_b && !_b.done && (_g = _a.return)) _g.call(_a);
|
|
3811
3791
|
}
|
|
3812
|
-
finally { if (
|
|
3792
|
+
finally { if (e_10) throw e_10.error; }
|
|
3813
3793
|
}
|
|
3814
3794
|
return [4 /*yield*/, runtime.load(text)];
|
|
3815
3795
|
case 1:
|
|
@@ -3832,14 +3812,14 @@ describe('service worker host', function () {
|
|
|
3832
3812
|
return [3 /*break*/, 3];
|
|
3833
3813
|
case 6: return [3 /*break*/, 9];
|
|
3834
3814
|
case 7:
|
|
3835
|
-
|
|
3836
|
-
|
|
3815
|
+
e_9_1 = _j.sent();
|
|
3816
|
+
e_9 = { error: e_9_1 };
|
|
3837
3817
|
return [3 /*break*/, 9];
|
|
3838
3818
|
case 8:
|
|
3839
3819
|
try {
|
|
3840
3820
|
if (_e && !_e.done && (_h = _d.return)) _h.call(_d);
|
|
3841
3821
|
}
|
|
3842
|
-
finally { if (
|
|
3822
|
+
finally { if (e_9) throw e_9.error; }
|
|
3843
3823
|
return [7 /*endfinally*/];
|
|
3844
3824
|
case 9: return [2 /*return*/];
|
|
3845
3825
|
}
|
|
@@ -3950,12 +3930,12 @@ describe('service worker host', function () {
|
|
|
3950
3930
|
_loop_1(testCase);
|
|
3951
3931
|
}
|
|
3952
3932
|
}
|
|
3953
|
-
catch (
|
|
3933
|
+
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
|
3954
3934
|
finally {
|
|
3955
3935
|
try {
|
|
3956
3936
|
if (DRIVE_CORPUS_1_1 && !DRIVE_CORPUS_1_1.done && (_a = DRIVE_CORPUS_1.return)) _a.call(DRIVE_CORPUS_1);
|
|
3957
3937
|
}
|
|
3958
|
-
finally { if (
|
|
3938
|
+
finally { if (e_6) throw e_6.error; }
|
|
3959
3939
|
}
|
|
3960
3940
|
/** The app a removal case names, as the corpus states it. */
|
|
3961
3941
|
var CORPUS_APP = {
|
|
@@ -4024,7 +4004,7 @@ describe('service worker host', function () {
|
|
|
4024
4004
|
return __generator(this, function (_a) {
|
|
4025
4005
|
files = {};
|
|
4026
4006
|
walk = function (members, prefix) {
|
|
4027
|
-
var
|
|
4007
|
+
var e_11, _a;
|
|
4028
4008
|
var _b;
|
|
4029
4009
|
try {
|
|
4030
4010
|
for (var _c = __values(members || []), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
@@ -4039,12 +4019,12 @@ describe('service worker host', function () {
|
|
|
4039
4019
|
: String((_b = member.code) !== null && _b !== void 0 ? _b : '');
|
|
4040
4020
|
}
|
|
4041
4021
|
}
|
|
4042
|
-
catch (
|
|
4022
|
+
catch (e_11_1) { e_11 = { error: e_11_1 }; }
|
|
4043
4023
|
finally {
|
|
4044
4024
|
try {
|
|
4045
4025
|
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
4046
4026
|
}
|
|
4047
|
-
finally { if (
|
|
4027
|
+
finally { if (e_11) throw e_11.error; }
|
|
4048
4028
|
}
|
|
4049
4029
|
};
|
|
4050
4030
|
walk(tree === null || tree === void 0 ? void 0 : tree.members, '');
|
|
@@ -4061,7 +4041,7 @@ describe('service worker host', function () {
|
|
|
4061
4041
|
*/
|
|
4062
4042
|
var deployRequest = function (testCase) { return __awaiter(void 0, void 0, void 0, function () {
|
|
4063
4043
|
var _a, requestPath, init, body, archive, form, _b, _c, _d, field, value;
|
|
4064
|
-
var
|
|
4044
|
+
var e_12, _e;
|
|
4065
4045
|
return __generator(this, function (_f) {
|
|
4066
4046
|
switch (_f.label) {
|
|
4067
4047
|
case 0:
|
|
@@ -4080,12 +4060,12 @@ describe('service worker host', function () {
|
|
|
4080
4060
|
form.set(field, String(value));
|
|
4081
4061
|
}
|
|
4082
4062
|
}
|
|
4083
|
-
catch (
|
|
4063
|
+
catch (e_12_1) { e_12 = { error: e_12_1 }; }
|
|
4084
4064
|
finally {
|
|
4085
4065
|
try {
|
|
4086
4066
|
if (_c && !_c.done && (_e = _b.return)) _e.call(_b);
|
|
4087
4067
|
}
|
|
4088
|
-
finally { if (
|
|
4068
|
+
finally { if (e_12) throw e_12.error; }
|
|
4089
4069
|
}
|
|
4090
4070
|
form.set('file', new Blob([new Uint8Array(archive)]), 'deploy.zip');
|
|
4091
4071
|
return [2 /*return*/, { requestPath: requestPath, init: { method: 'POST', body: form } }];
|
|
@@ -4094,8 +4074,8 @@ describe('service worker host', function () {
|
|
|
4094
4074
|
}); };
|
|
4095
4075
|
var _loop_2 = function (testCase) {
|
|
4096
4076
|
it(testCase.id, function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
4097
|
-
var worker, _a, requestPath, init, response, listing, ids, _b, _c, path_6, file,
|
|
4098
|
-
var
|
|
4077
|
+
var worker, _a, requestPath, init, response, listing, ids, _b, _c, path_6, file, e_13_1, _d, _e, path_7, file, e_14_1;
|
|
4078
|
+
var e_13, _f, e_14, _g;
|
|
4099
4079
|
var _h, _j, _k;
|
|
4100
4080
|
return __generator(this, function (_l) {
|
|
4101
4081
|
switch (_l.label) {
|
|
@@ -4147,14 +4127,14 @@ describe('service worker host', function () {
|
|
|
4147
4127
|
return [3 /*break*/, 9];
|
|
4148
4128
|
case 12: return [3 /*break*/, 15];
|
|
4149
4129
|
case 13:
|
|
4150
|
-
|
|
4151
|
-
|
|
4130
|
+
e_13_1 = _l.sent();
|
|
4131
|
+
e_13 = { error: e_13_1 };
|
|
4152
4132
|
return [3 /*break*/, 15];
|
|
4153
4133
|
case 14:
|
|
4154
4134
|
try {
|
|
4155
4135
|
if (_c && !_c.done && (_f = _b.return)) _f.call(_b);
|
|
4156
4136
|
}
|
|
4157
|
-
finally { if (
|
|
4137
|
+
finally { if (e_13) throw e_13.error; }
|
|
4158
4138
|
return [7 /*endfinally*/];
|
|
4159
4139
|
case 15:
|
|
4160
4140
|
_l.trys.push([15, 20, 21, 22]);
|
|
@@ -4173,14 +4153,14 @@ describe('service worker host', function () {
|
|
|
4173
4153
|
return [3 /*break*/, 16];
|
|
4174
4154
|
case 19: return [3 /*break*/, 22];
|
|
4175
4155
|
case 20:
|
|
4176
|
-
|
|
4177
|
-
|
|
4156
|
+
e_14_1 = _l.sent();
|
|
4157
|
+
e_14 = { error: e_14_1 };
|
|
4178
4158
|
return [3 /*break*/, 22];
|
|
4179
4159
|
case 21:
|
|
4180
4160
|
try {
|
|
4181
4161
|
if (_e && !_e.done && (_g = _d.return)) _g.call(_d);
|
|
4182
4162
|
}
|
|
4183
|
-
finally { if (
|
|
4163
|
+
finally { if (e_14) throw e_14.error; }
|
|
4184
4164
|
return [7 /*endfinally*/];
|
|
4185
4165
|
case 22: return [2 /*return*/];
|
|
4186
4166
|
}
|
|
@@ -4213,12 +4193,12 @@ describe('service worker host', function () {
|
|
|
4213
4193
|
_loop_2(testCase);
|
|
4214
4194
|
}
|
|
4215
4195
|
}
|
|
4216
|
-
catch (
|
|
4196
|
+
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
|
4217
4197
|
finally {
|
|
4218
4198
|
try {
|
|
4219
4199
|
if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
|
|
4220
4200
|
}
|
|
4221
|
-
finally { if (
|
|
4201
|
+
finally { if (e_7) throw e_7.error; }
|
|
4222
4202
|
}
|
|
4223
4203
|
});
|
|
4224
4204
|
/**
|
|
@@ -4230,7 +4210,7 @@ describe('service worker host', function () {
|
|
|
4230
4210
|
* are the package's.
|
|
4231
4211
|
*/
|
|
4232
4212
|
describe('identity and record conformance', function () {
|
|
4233
|
-
var
|
|
4213
|
+
var e_15, _a;
|
|
4234
4214
|
/**
|
|
4235
4215
|
* Boots the worker from a fixture, the way the page does: the case's own
|
|
4236
4216
|
* identity, and `subject: null` for no implicit caller, so a case that
|
|
@@ -4263,7 +4243,7 @@ describe('service worker host', function () {
|
|
|
4263
4243
|
};
|
|
4264
4244
|
/** The request a case describes, as HTTP: path parameters filled, token sent. */
|
|
4265
4245
|
var httpForCase = function (testCase) {
|
|
4266
|
-
var
|
|
4246
|
+
var e_16, _a;
|
|
4267
4247
|
var _b = __read(testCase.route.split(' '), 2), method = _b[0], routePath = _b[1];
|
|
4268
4248
|
var filled = routePath.replace(/\{(\w+)\}/g, function (_match, name) {
|
|
4269
4249
|
return encodeURIComponent(String((testCase.request.params || {})[name]));
|
|
@@ -4276,12 +4256,12 @@ describe('service worker host', function () {
|
|
|
4276
4256
|
query.set(key, String(value));
|
|
4277
4257
|
}
|
|
4278
4258
|
}
|
|
4279
|
-
catch (
|
|
4259
|
+
catch (e_16_1) { e_16 = { error: e_16_1 }; }
|
|
4280
4260
|
finally {
|
|
4281
4261
|
try {
|
|
4282
4262
|
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
4283
4263
|
}
|
|
4284
|
-
finally { if (
|
|
4264
|
+
finally { if (e_16) throw e_16.error; }
|
|
4285
4265
|
}
|
|
4286
4266
|
var suffix = query.toString() ? "?".concat(query) : '';
|
|
4287
4267
|
var headers = {};
|
|
@@ -4403,12 +4383,12 @@ describe('service worker host', function () {
|
|
|
4403
4383
|
_loop_3(testCase);
|
|
4404
4384
|
}
|
|
4405
4385
|
}
|
|
4406
|
-
catch (
|
|
4386
|
+
catch (e_15_1) { e_15 = { error: e_15_1 }; }
|
|
4407
4387
|
finally {
|
|
4408
4388
|
try {
|
|
4409
4389
|
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
4410
4390
|
}
|
|
4411
|
-
finally { if (
|
|
4391
|
+
finally { if (e_15) throw e_15.error; }
|
|
4412
4392
|
}
|
|
4413
4393
|
it('answers as the fixture administrator when no subject was chosen', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
4414
4394
|
var worker, response, rules;
|
|
@@ -21,7 +21,7 @@ declare module '@sasjs/utils/types/config' {
|
|
|
21
21
|
*/
|
|
22
22
|
mockWebBasePath?: string;
|
|
23
23
|
/**
|
|
24
|
-
* Whether the mock site carries the API console at `<base>/api/`.
|
|
24
|
+
* Whether the mock site carries the API console at `<base>/api-console/`.
|
|
25
25
|
*
|
|
26
26
|
* On unless it is set to false: the console is how a demonstration site lets
|
|
27
27
|
* a visitor try the API it is a mock of, and a page nothing links to costs
|