@sasjs/cli 6.28.0 → 6.30.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 +57 -4
- package/build/commands/build/internal/mockSite/mockSite.js +7 -6
- package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +1 -0
- package/build/commands/build/internal/mockSite/spec/sw.spec.js +279 -0
- package/build/commands/create/spec/create.spec.js +22 -18
- package/build/commands/deploy/spec/cbd.spec.js +149 -65
- package/build/commands/shared/createLintConfigFile.js +24 -2
- package/build/commands/shared/spec/createLintConfigFile.spec.d.ts +1 -0
- package/build/commands/shared/spec/createLintConfigFile.spec.js +132 -0
- package/build/utils/test.d.ts +12 -0
- package/build/utils/test.js +71 -4
- package/build/utils/utils.d.ts +8 -0
- package/build/utils/utils.js +16 -4
- package/package.json +2 -2
- package/build/commands/create/spec/angularAppFiles.d.ts +0 -2
- package/build/commands/create/spec/angularAppFiles.js +0 -119
- package/build/commands/create/spec/minimalAppFiles.d.ts +0 -2
- package/build/commands/create/spec/minimalAppFiles.js +0 -44
- package/build/commands/create/spec/reactAppFiles.d.ts +0 -2
- package/build/commands/create/spec/reactAppFiles.js +0 -93
|
@@ -946,6 +946,58 @@ const homeService = API.createHomeService(async () => {
|
|
|
946
946
|
}
|
|
947
947
|
})
|
|
948
948
|
|
|
949
|
+
/**
|
|
950
|
+
* The lint rules a SAS file is linted against: the nearest `.sasjslint` at or
|
|
951
|
+
* above its folder on the drive.
|
|
952
|
+
*
|
|
953
|
+
* This is the server's rule, applied here so the demo behaves like the
|
|
954
|
+
* installation it demonstrates: a folder in the tree carries rules for
|
|
955
|
+
* everything beneath it, and changing a rule is a plain edit of that file. It
|
|
956
|
+
* is why the build seeds a `.sasjslint` at the drive root - a file that is
|
|
957
|
+
* displayed but never read would be worse than absent.
|
|
958
|
+
*
|
|
959
|
+
* Paths go through the package's own normaliser, so a backslashed or escaping
|
|
960
|
+
* path resolves the way the drive routes resolve it rather than by a second set
|
|
961
|
+
* of string rules that could disagree with them.
|
|
962
|
+
*
|
|
963
|
+
* A file that is missing, is not JSON, or parses to something that is not an
|
|
964
|
+
* object is skipped and the walk continues upwards - a broken rules file must
|
|
965
|
+
* never break linting or formatting. A walk that reaches the root with nothing
|
|
966
|
+
* to show returns undefined, which lints against the linter's own defaults: the
|
|
967
|
+
* answer for a drive with no rules file at all.
|
|
968
|
+
*/
|
|
969
|
+
const LINT_CONFIG_FILE = '.sasjslint'
|
|
970
|
+
|
|
971
|
+
const lintConfig = async (filePath) => {
|
|
972
|
+
// A path that escapes the drive has no folder to walk from, and a buffer that
|
|
973
|
+
// names no file has none either: both take the drive root's rules, which is
|
|
974
|
+
// what a server answers for each - a refused path there falls back to the
|
|
975
|
+
// root file rather than to the linter's defaults.
|
|
976
|
+
let folder = API.dirname(API.normaliseDrivePath(filePath) ?? '/')
|
|
977
|
+
for (;;) {
|
|
978
|
+
const configPath =
|
|
979
|
+
folder === '/' ? `/${LINT_CONFIG_FILE}` : `${folder}/${LINT_CONFIG_FILE}`
|
|
980
|
+
const contents = await DRIVE_STORE.read(configPath).catch(() => null)
|
|
981
|
+
const parsed = parseLintConfig(contents)
|
|
982
|
+
if (parsed) return new CODE.LintConfig(parsed)
|
|
983
|
+
if (folder === '/') return undefined
|
|
984
|
+
folder = API.dirname(folder)
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/** A `.sasjslint` as the object a configuration is built from, or null. */
|
|
989
|
+
const parseLintConfig = (contents) => {
|
|
990
|
+
if (contents === null || contents === undefined) return null
|
|
991
|
+
const text =
|
|
992
|
+
typeof contents === 'string' ? contents : new TextDecoder().decode(contents)
|
|
993
|
+
try {
|
|
994
|
+
const parsed = JSON.parse(text)
|
|
995
|
+
return parsed && typeof parsed === 'object' ? parsed : null
|
|
996
|
+
} catch (e) {
|
|
997
|
+
return null
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
949
1001
|
const SERVICES = {
|
|
950
1002
|
drive: API.createDriveService(DRIVE_STORE),
|
|
951
1003
|
// The package's browser fixture states one run time; this host serves two, and
|
|
@@ -970,10 +1022,11 @@ const SERVICES = {
|
|
|
970
1022
|
directory: API.createDirectoryService(IDENTITY_STORE, directorySource),
|
|
971
1023
|
appStream: APP_STREAM,
|
|
972
1024
|
home: homeService,
|
|
973
|
-
// the package's own code service over the bundled linter
|
|
974
|
-
//
|
|
975
|
-
//
|
|
976
|
-
|
|
1025
|
+
// the package's own code service over the bundled linter, with the drive's
|
|
1026
|
+
// own rules: a buffer is linted and formatted against the nearest
|
|
1027
|
+
// `.sasjslint` at or above its folder, so editing that file changes the
|
|
1028
|
+
// editor here exactly as it does on a server
|
|
1029
|
+
code: CODE.createCodeService({ lintConfig })
|
|
977
1030
|
}
|
|
978
1031
|
|
|
979
1032
|
/** The routes this host performs itself, because the answer is not JSON. */
|
|
@@ -130,10 +130,11 @@ var toPosix = function (value) { return value.split(path_1.default.sep).join('/'
|
|
|
130
130
|
* configuration by walking a filesystem - but the linter's core (`lintText`,
|
|
131
131
|
* `formatText`) reads only the text it is given and the configuration it is
|
|
132
132
|
* passed, so the two can run anywhere the module graph can be built. This
|
|
133
|
-
* bundle is that graph: the package's own code service, the
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
133
|
+
* bundle is that graph: the package's own code service, the configuration class
|
|
134
|
+
* a host builds from its own rules file, the linter behind both, and shims for
|
|
135
|
+
* the four node modules reached at import time (`path` and `os` for resolution,
|
|
136
|
+
* `fs` and `@sasjs/utils/file` for the linter's own filesystem lookup, which
|
|
137
|
+
* this host never makes - it hands the routes the rules it read from the drive).
|
|
137
138
|
*
|
|
138
139
|
* It is built from the installed @sasjs/api and @sasjs/lint on every mock
|
|
139
140
|
* build, so the rules a client sees are the version the CLI resolved - a
|
|
@@ -142,8 +143,8 @@ var toPosix = function (value) { return value.split(path_1.default.sep).join('/'
|
|
|
142
143
|
var LINT_BUNDLE_FILES = {
|
|
143
144
|
'entry.js': [
|
|
144
145
|
"import './process.js'",
|
|
145
|
-
"import { createCodeService } from '@sasjs/api/code'",
|
|
146
|
-
'globalThis.SASjsCode = { createCodeService }'
|
|
146
|
+
"import { createCodeService, LintConfig } from '@sasjs/api/code'",
|
|
147
|
+
'globalThis.SASjsCode = { createCodeService, LintConfig }'
|
|
147
148
|
].join('\n'),
|
|
148
149
|
'process.js': [
|
|
149
150
|
'// the linter reads process.env for a default and process.cwd() for a root;',
|
|
@@ -297,6 +297,7 @@ describe('browser-hosted build', function () {
|
|
|
297
297
|
vm.createContext(realm);
|
|
298
298
|
vm.runInContext(bundle, realm);
|
|
299
299
|
expect(typeof realm.SASjsCode.createCodeService).toBe('function');
|
|
300
|
+
expect(typeof realm.SASjsCode.LintConfig).toBe('function');
|
|
300
301
|
return [2 /*return*/];
|
|
301
302
|
}
|
|
302
303
|
});
|
|
@@ -1412,6 +1412,285 @@ describe('service worker host', function () {
|
|
|
1412
1412
|
}
|
|
1413
1413
|
});
|
|
1414
1414
|
}); });
|
|
1415
|
+
/**
|
|
1416
|
+
* The rules are the drive's, not the linter's defaults.
|
|
1417
|
+
*
|
|
1418
|
+
* The Studio shows a visitor the drive's `.sasjslint` and invites them to
|
|
1419
|
+
* edit it, so a rules file nothing read would be a displayed lie. Each case
|
|
1420
|
+
* below changes a rule and reads the answer back through the route the
|
|
1421
|
+
* editor calls, because "the resolver was passed" is not the claim - "the
|
|
1422
|
+
* squiggle went away" is.
|
|
1423
|
+
*/
|
|
1424
|
+
describe('the drive’s .sasjslint', function () {
|
|
1425
|
+
var rootConfig = '/sasjs/drive/files/.sasjslint';
|
|
1426
|
+
/** 81 characters: over the default 80, under the 100 these cases set. */
|
|
1427
|
+
var longLine = 'x'.repeat(81);
|
|
1428
|
+
var code = "data _null_;\n".concat(longLine, "\nrun;\n");
|
|
1429
|
+
var tooLong = function (diagnostics) {
|
|
1430
|
+
return diagnostics.filter(function (diagnostic) {
|
|
1431
|
+
return String(diagnostic.message).startsWith('Line exceeds maximum length');
|
|
1432
|
+
});
|
|
1433
|
+
};
|
|
1434
|
+
var lint = function (worker, body) { return __awaiter(void 0, void 0, void 0, function () {
|
|
1435
|
+
var response;
|
|
1436
|
+
return __generator(this, function (_a) {
|
|
1437
|
+
switch (_a.label) {
|
|
1438
|
+
case 0: return [4 /*yield*/, worker.dispatch('/SASjsApi/code/lint', runRequest(body))];
|
|
1439
|
+
case 1:
|
|
1440
|
+
response = _a.sent();
|
|
1441
|
+
expect(response.status).toBe(200);
|
|
1442
|
+
return [2 /*return*/, response.json()];
|
|
1443
|
+
}
|
|
1444
|
+
});
|
|
1445
|
+
}); };
|
|
1446
|
+
it('lints against the rules file at the drive root', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1447
|
+
var worker, _a, _b, _c, _d;
|
|
1448
|
+
var _e, _f;
|
|
1449
|
+
return __generator(this, function (_g) {
|
|
1450
|
+
switch (_g.label) {
|
|
1451
|
+
case 0:
|
|
1452
|
+
worker = seeded();
|
|
1453
|
+
return [4 /*yield*/, worker.runtime.load((_e = {},
|
|
1454
|
+
_e[rootConfig] = JSON.stringify({ maxLineLength: 100 }),
|
|
1455
|
+
_e[drivePath('program.sas')] = code,
|
|
1456
|
+
_e))];
|
|
1457
|
+
case 1:
|
|
1458
|
+
_g.sent();
|
|
1459
|
+
_a = expect;
|
|
1460
|
+
_b = tooLong;
|
|
1461
|
+
return [4 /*yield*/, lint(worker, { code: code, filePath: appLoc + '/program.sas' })];
|
|
1462
|
+
case 2:
|
|
1463
|
+
_a.apply(void 0, [_b.apply(void 0, [_g.sent()])]).toHaveLength(0);
|
|
1464
|
+
// the same buffer with the default 80 in place: the finding the rule
|
|
1465
|
+
// exists to report
|
|
1466
|
+
return [4 /*yield*/, worker.runtime.load((_f = {},
|
|
1467
|
+
_f[rootConfig] = JSON.stringify({ maxLineLength: 80 }),
|
|
1468
|
+
_f[drivePath('program.sas')] = code,
|
|
1469
|
+
_f))];
|
|
1470
|
+
case 3:
|
|
1471
|
+
// the same buffer with the default 80 in place: the finding the rule
|
|
1472
|
+
// exists to report
|
|
1473
|
+
_g.sent();
|
|
1474
|
+
_c = expect;
|
|
1475
|
+
_d = tooLong;
|
|
1476
|
+
return [4 /*yield*/, lint(worker, { code: code, filePath: appLoc + '/program.sas' })];
|
|
1477
|
+
case 4:
|
|
1478
|
+
_c.apply(void 0, [_d.apply(void 0, [_g.sent()])]).toHaveLength(1);
|
|
1479
|
+
return [2 /*return*/];
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
}); });
|
|
1483
|
+
it('re-reads the rules per request, so an edit takes effect', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1484
|
+
var worker, path, _a, _b, form, patched, _c, _d;
|
|
1485
|
+
var _e;
|
|
1486
|
+
return __generator(this, function (_f) {
|
|
1487
|
+
switch (_f.label) {
|
|
1488
|
+
case 0:
|
|
1489
|
+
worker = seeded();
|
|
1490
|
+
path = appLoc + '/program.sas';
|
|
1491
|
+
return [4 /*yield*/, worker.runtime.load((_e = {},
|
|
1492
|
+
_e[rootConfig] = JSON.stringify({ maxLineLength: 80 }),
|
|
1493
|
+
_e[drivePath('program.sas')] = code,
|
|
1494
|
+
_e))];
|
|
1495
|
+
case 1:
|
|
1496
|
+
_f.sent();
|
|
1497
|
+
_a = expect;
|
|
1498
|
+
_b = tooLong;
|
|
1499
|
+
return [4 /*yield*/, lint(worker, { code: code, filePath: path })];
|
|
1500
|
+
case 2:
|
|
1501
|
+
_a.apply(void 0, [_b.apply(void 0, [_f.sent()])]).toHaveLength(1);
|
|
1502
|
+
form = new FormData();
|
|
1503
|
+
form.set('file', new Blob([JSON.stringify({ maxLineLength: 100 })], {
|
|
1504
|
+
type: 'application/json'
|
|
1505
|
+
}), '.sasjslint');
|
|
1506
|
+
return [4 /*yield*/, worker.dispatch("/SASjsApi/drive/file?_filePath=".concat(encodeURIComponent('/.sasjslint')), { method: 'PATCH', body: form })];
|
|
1507
|
+
case 3:
|
|
1508
|
+
patched = _f.sent();
|
|
1509
|
+
expect(patched.status).toBe(200);
|
|
1510
|
+
_c = expect;
|
|
1511
|
+
_d = tooLong;
|
|
1512
|
+
return [4 /*yield*/, lint(worker, { code: code, filePath: path })];
|
|
1513
|
+
case 4:
|
|
1514
|
+
_c.apply(void 0, [_d.apply(void 0, [_f.sent()])]).toHaveLength(0);
|
|
1515
|
+
return [2 /*return*/];
|
|
1516
|
+
}
|
|
1517
|
+
});
|
|
1518
|
+
}); });
|
|
1519
|
+
it('gives a folder its own rules for everything beneath it', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1520
|
+
var worker, _a, _b, _c, _d;
|
|
1521
|
+
var _e;
|
|
1522
|
+
return __generator(this, function (_f) {
|
|
1523
|
+
switch (_f.label) {
|
|
1524
|
+
case 0:
|
|
1525
|
+
worker = seeded();
|
|
1526
|
+
return [4 /*yield*/, worker.runtime.load((_e = {},
|
|
1527
|
+
_e[rootConfig] = JSON.stringify({ maxLineLength: 80 }),
|
|
1528
|
+
// the nearer file relaxes the rule for this folder alone
|
|
1529
|
+
_e["/sasjs/drive/files".concat(appLoc, "/.sasjslint")] = JSON.stringify({
|
|
1530
|
+
maxLineLength: 200
|
|
1531
|
+
}),
|
|
1532
|
+
_e))];
|
|
1533
|
+
case 1:
|
|
1534
|
+
_f.sent();
|
|
1535
|
+
_a = expect;
|
|
1536
|
+
_b = tooLong;
|
|
1537
|
+
return [4 /*yield*/, lint(worker, {
|
|
1538
|
+
code: code,
|
|
1539
|
+
filePath: "".concat(appLoc, "/services/public/program.sas")
|
|
1540
|
+
})];
|
|
1541
|
+
case 2:
|
|
1542
|
+
_a.apply(void 0, [_b.apply(void 0, [_f.sent()])]).toHaveLength(0);
|
|
1543
|
+
// outside that folder the root file still applies
|
|
1544
|
+
_c = expect;
|
|
1545
|
+
_d = tooLong;
|
|
1546
|
+
return [4 /*yield*/, lint(worker, {
|
|
1547
|
+
code: code,
|
|
1548
|
+
filePath: '/home/sasjsadmin/program.sas'
|
|
1549
|
+
})];
|
|
1550
|
+
case 3:
|
|
1551
|
+
// outside that folder the root file still applies
|
|
1552
|
+
_c.apply(void 0, [_d.apply(void 0, [_f.sent()])]).toHaveLength(1);
|
|
1553
|
+
return [2 /*return*/];
|
|
1554
|
+
}
|
|
1555
|
+
});
|
|
1556
|
+
}); });
|
|
1557
|
+
it('finds the rules for a path spelled the way a Windows client sends it', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1558
|
+
var worker, _a, _b;
|
|
1559
|
+
var _c;
|
|
1560
|
+
return __generator(this, function (_d) {
|
|
1561
|
+
switch (_d.label) {
|
|
1562
|
+
case 0:
|
|
1563
|
+
worker = seeded();
|
|
1564
|
+
return [4 /*yield*/, worker.runtime.load((_c = {},
|
|
1565
|
+
_c["/sasjs/drive/files".concat(appLoc, "/.sasjslint")] = JSON.stringify({
|
|
1566
|
+
maxLineLength: 100
|
|
1567
|
+
}),
|
|
1568
|
+
_c))];
|
|
1569
|
+
case 1:
|
|
1570
|
+
_d.sent();
|
|
1571
|
+
_a = expect;
|
|
1572
|
+
_b = tooLong;
|
|
1573
|
+
return [4 /*yield*/, lint(worker, {
|
|
1574
|
+
code: code,
|
|
1575
|
+
filePath: "".concat(appLoc, "\\services\\public\\program.sas")
|
|
1576
|
+
})];
|
|
1577
|
+
case 2:
|
|
1578
|
+
_a.apply(void 0, [_b.apply(void 0, [_d.sent()])]).toHaveLength(0);
|
|
1579
|
+
return [2 /*return*/];
|
|
1580
|
+
}
|
|
1581
|
+
});
|
|
1582
|
+
}); });
|
|
1583
|
+
it('skips a rules file that does not parse and uses the one above', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1584
|
+
var worker, _a, _b;
|
|
1585
|
+
var _c;
|
|
1586
|
+
return __generator(this, function (_d) {
|
|
1587
|
+
switch (_d.label) {
|
|
1588
|
+
case 0:
|
|
1589
|
+
worker = seeded();
|
|
1590
|
+
return [4 /*yield*/, worker.runtime.load((_c = {},
|
|
1591
|
+
_c[rootConfig] = JSON.stringify({ maxLineLength: 100 }),
|
|
1592
|
+
_c["/sasjs/drive/files".concat(appLoc, "/.sasjslint")] = '{ this is not json',
|
|
1593
|
+
_c))
|
|
1594
|
+
// a broken near file must not break linting, and must not silently
|
|
1595
|
+
// replace the rules that were working
|
|
1596
|
+
];
|
|
1597
|
+
case 1:
|
|
1598
|
+
_d.sent();
|
|
1599
|
+
// a broken near file must not break linting, and must not silently
|
|
1600
|
+
// replace the rules that were working
|
|
1601
|
+
_a = expect;
|
|
1602
|
+
_b = tooLong;
|
|
1603
|
+
return [4 /*yield*/, lint(worker, {
|
|
1604
|
+
code: code,
|
|
1605
|
+
filePath: "".concat(appLoc, "/services/public/program.sas")
|
|
1606
|
+
})];
|
|
1607
|
+
case 2:
|
|
1608
|
+
// a broken near file must not break linting, and must not silently
|
|
1609
|
+
// replace the rules that were working
|
|
1610
|
+
_a.apply(void 0, [_b.apply(void 0, [_d.sent()])]).toHaveLength(0);
|
|
1611
|
+
return [2 /*return*/];
|
|
1612
|
+
}
|
|
1613
|
+
});
|
|
1614
|
+
}); });
|
|
1615
|
+
it('takes the drive root rules for a buffer with no file, and for a path that escapes the drive', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1616
|
+
var worker, _a, _b, _c, _d;
|
|
1617
|
+
var _e;
|
|
1618
|
+
return __generator(this, function (_f) {
|
|
1619
|
+
switch (_f.label) {
|
|
1620
|
+
case 0:
|
|
1621
|
+
worker = seeded();
|
|
1622
|
+
return [4 /*yield*/, worker.runtime.load((_e = {},
|
|
1623
|
+
_e[rootConfig] = JSON.stringify({ maxLineLength: 100 }),
|
|
1624
|
+
_e))];
|
|
1625
|
+
case 1:
|
|
1626
|
+
_f.sent();
|
|
1627
|
+
_a = expect;
|
|
1628
|
+
_b = tooLong;
|
|
1629
|
+
return [4 /*yield*/, lint(worker, { code: code })];
|
|
1630
|
+
case 2:
|
|
1631
|
+
_a.apply(void 0, [_b.apply(void 0, [_f.sent()])]).toHaveLength(0);
|
|
1632
|
+
_c = expect;
|
|
1633
|
+
_d = tooLong;
|
|
1634
|
+
return [4 /*yield*/, lint(worker, {
|
|
1635
|
+
code: code,
|
|
1636
|
+
filePath: '../../outside/program.sas'
|
|
1637
|
+
})];
|
|
1638
|
+
case 3:
|
|
1639
|
+
_c.apply(void 0, [_d.apply(void 0, [_f.sent()])]).toHaveLength(0);
|
|
1640
|
+
return [2 /*return*/];
|
|
1641
|
+
}
|
|
1642
|
+
});
|
|
1643
|
+
}); });
|
|
1644
|
+
it('falls back to the linter defaults when the drive has no rules file', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1645
|
+
var worker, _a, _b;
|
|
1646
|
+
var _c;
|
|
1647
|
+
return __generator(this, function (_d) {
|
|
1648
|
+
switch (_d.label) {
|
|
1649
|
+
case 0:
|
|
1650
|
+
worker = seeded();
|
|
1651
|
+
return [4 /*yield*/, worker.runtime.load((_c = {}, _c[drivePath('program.sas')] = code, _c))];
|
|
1652
|
+
case 1:
|
|
1653
|
+
_d.sent();
|
|
1654
|
+
_a = expect;
|
|
1655
|
+
_b = tooLong;
|
|
1656
|
+
return [4 /*yield*/, lint(worker, {
|
|
1657
|
+
code: code,
|
|
1658
|
+
filePath: "".concat(appLoc, "/program.sas")
|
|
1659
|
+
})];
|
|
1660
|
+
case 2:
|
|
1661
|
+
_a.apply(void 0, [_b.apply(void 0, [_d.sent()])]).toHaveLength(1);
|
|
1662
|
+
return [2 /*return*/];
|
|
1663
|
+
}
|
|
1664
|
+
});
|
|
1665
|
+
}); });
|
|
1666
|
+
it('formats with the rules file too', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1667
|
+
var worker, formatted, _a;
|
|
1668
|
+
var _b;
|
|
1669
|
+
return __generator(this, function (_c) {
|
|
1670
|
+
switch (_c.label) {
|
|
1671
|
+
case 0:
|
|
1672
|
+
worker = seeded();
|
|
1673
|
+
return [4 /*yield*/, worker.runtime.load((_b = {},
|
|
1674
|
+
_b[rootConfig] = JSON.stringify({
|
|
1675
|
+
hasDoxygenHeader: true,
|
|
1676
|
+
defaultHeader: '/**\n @brief the drive header\n**/'
|
|
1677
|
+
}),
|
|
1678
|
+
_b))];
|
|
1679
|
+
case 1:
|
|
1680
|
+
_c.sent();
|
|
1681
|
+
return [4 /*yield*/, worker.dispatch('/SASjsApi/code/format', runRequest({ code: code, filePath: '/home/sasjsadmin/program.sas' }))];
|
|
1682
|
+
case 2:
|
|
1683
|
+
formatted = _c.sent();
|
|
1684
|
+
expect(formatted.status).toBe(200);
|
|
1685
|
+
_a = expect;
|
|
1686
|
+
return [4 /*yield*/, formatted.text()];
|
|
1687
|
+
case 3:
|
|
1688
|
+
_a.apply(void 0, [_c.sent()]).toContain('@brief the drive header');
|
|
1689
|
+
return [2 /*return*/];
|
|
1690
|
+
}
|
|
1691
|
+
});
|
|
1692
|
+
}); });
|
|
1693
|
+
});
|
|
1415
1694
|
it('keeps an app’s URL and tiles inside the folder the site is served from', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1416
1695
|
var worker, archive, form, published, app, listing, apps, landing, _a, page, _b;
|
|
1417
1696
|
return __generator(this, function (_c) {
|
|
@@ -44,11 +44,9 @@ var path_1 = __importDefault(require("path"));
|
|
|
44
44
|
var test_1 = require("../../../utils/test");
|
|
45
45
|
var utils_1 = require("@sasjs/utils");
|
|
46
46
|
var config_1 = require("../../../utils/config");
|
|
47
|
-
var minimalAppFiles_1 = require("./minimalAppFiles");
|
|
48
|
-
var reactAppFiles_1 = require("./reactAppFiles");
|
|
49
|
-
var angularAppFiles_1 = require("./angularAppFiles");
|
|
50
47
|
var create_1 = require("../create");
|
|
51
48
|
var utils_2 = require("../../../utils");
|
|
49
|
+
var utils_3 = require("../../../utils/utils");
|
|
52
50
|
describe('sasjs create', function () {
|
|
53
51
|
beforeAll(function () {
|
|
54
52
|
dotenv_1.default.config();
|
|
@@ -249,28 +247,34 @@ describe('sasjs create', function () {
|
|
|
249
247
|
}); });
|
|
250
248
|
});
|
|
251
249
|
var verifyCreateWeb = function (appName, appType) { return __awaiter(void 0, void 0, void 0, function () {
|
|
252
|
-
var
|
|
250
|
+
var template, docsCandidates, present;
|
|
253
251
|
return __generator(this, function (_a) {
|
|
254
252
|
switch (_a.label) {
|
|
255
|
-
case 0:
|
|
256
|
-
fileStructure = appType === 'minimal'
|
|
257
|
-
? minimalAppFiles_1.minimalAppFiles
|
|
258
|
-
: appType === 'react'
|
|
259
|
-
? reactAppFiles_1.reactAppFiles
|
|
260
|
-
: appType === 'angular'
|
|
261
|
-
? angularAppFiles_1.angularAppFiles
|
|
262
|
-
: minimalAppFiles_1.minimalAppFiles;
|
|
263
|
-
fileStructure.folderName = appName;
|
|
264
|
-
return [4 /*yield*/, (0, test_1.verifyFolder)(fileStructure)];
|
|
253
|
+
case 0: return [4 /*yield*/, (0, test_1.getTemplateFileTree)(utils_3.seedAppUrls[appType])];
|
|
265
254
|
case 1:
|
|
255
|
+
template = _a.sent();
|
|
256
|
+
template.folderName = appName;
|
|
257
|
+
return [4 /*yield*/, (0, test_1.verifyFolder)(template)];
|
|
258
|
+
case 2:
|
|
266
259
|
_a.sent();
|
|
267
260
|
return [4 /*yield*/, (0, test_1.verifyPackageJsonContent)(appName)
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
261
|
+
// The docs are a submodule fetched after the archive, and the archive carries
|
|
262
|
+
// the folder empty - so the derived tree can only assert that the folder is
|
|
263
|
+
// there. A create whose docs download silently failed used to pass; assert
|
|
264
|
+
// the site itself arrived. Every template gets the docs (create routes all
|
|
265
|
+
// three through a create*App that passes the docs URL), and the path differs:
|
|
266
|
+
// react keeps them under `public`, the others at the root.
|
|
271
267
|
];
|
|
272
|
-
case
|
|
268
|
+
case 3:
|
|
273
269
|
_a.sent();
|
|
270
|
+
docsCandidates = [
|
|
271
|
+
path_1.default.join(process.projectDir, appName, 'public', 'docs', 'index.html'),
|
|
272
|
+
path_1.default.join(process.projectDir, appName, 'docs', 'index.html')
|
|
273
|
+
];
|
|
274
|
+
return [4 /*yield*/, Promise.all(docsCandidates.map(function (candidate) { return (0, utils_1.fileExists)(candidate); }))];
|
|
275
|
+
case 4:
|
|
276
|
+
present = _a.sent();
|
|
277
|
+
expect(present.some(Boolean)).toEqual(true);
|
|
274
278
|
// A seed app carries its dependencies in the template's package.json, so the
|
|
275
279
|
// command create issues for it is a plain install - the same trade as above:
|
|
276
280
|
// the stub writes what npm would, and this asserts the CLI asked for it.
|