@serverkgg/bridge 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/rules/package.md +60 -0
- package/src/blog/articleFrontmatter.test.ts +10 -10
- package/src/blog/compile.test.ts +9 -9
- package/src/bot/botDeploy.test.ts +16 -16
- package/src/bot/botEnvironment.test.ts +8 -8
- package/src/bot/botIdentity.test.ts +12 -12
- package/src/bot/botPanel.test.ts +41 -11
- package/src/bot/botPanel.ts +13 -5
- package/src/bot/botState.test.ts +3 -3
- package/src/bot/botTerminal.test.ts +5 -5
- package/src/bot/botVariables.test.ts +8 -8
- package/src/bot/detect/botToken.test.ts +6 -6
- package/src/bot/detect/detect.test.ts +21 -21
- package/src/bot/detect/detectTable.test.ts +5 -5
- package/src/bot/detect/envExample.test.ts +2 -2
- package/src/bot/detect/envScan.test.ts +4 -4
- package/src/bot/detect/inferBotPlatform.test.ts +4 -4
- package/src/bot/detect/keys.test.ts +3 -3
- package/src/bot/detect/sensitiveKey.test.ts +5 -5
- package/src/bot/detect/serverkJson.test.ts +5 -5
- package/src/catalogs/curseforge.test.ts +35 -35
- package/src/catalogs/thunderstore.test.ts +9 -9
- package/src/cli/detectTable.test.ts +3 -3
- package/src/cli/link.test.ts +11 -11
- package/src/cli/main.ts +8 -0
- package/src/cli/rules.test.ts +198 -0
- package/src/cli/rules.ts +188 -0
- package/src/cli/validate.test.ts +123 -29
- package/src/cli/validate.ts +37 -1
- package/src/codecs/ini.test.ts +13 -13
- package/src/codecs/javaProperties.test.ts +11 -11
- package/src/codecs/sourceCfg.test.ts +14 -14
- package/src/codecs/xmlProperties.test.ts +10 -10
- package/src/context/bridgeContext.ts +6 -1
- package/src/core/bridgeAction.ts +2 -0
- package/src/core/bridgeActivity.ts +9 -0
- package/src/core/bridgeAvailability.ts +10 -0
- package/src/core/bridgePort.test.ts +6 -6
- package/src/core/bridgeUserError.test.ts +13 -3
- package/src/core/bridgeUserError.ts +6 -2
- package/src/core/bridgeValue.test.ts +5 -5
- package/src/core/describeDownloadFailure.test.ts +58 -0
- package/src/core/describeDownloadFailure.ts +69 -0
- package/src/core/index.ts +3 -0
- package/src/guides/markdown.test.ts +31 -31
- package/src/install/installStamp.test.ts +9 -9
- package/src/manifest/extension.ts +36 -0
- package/src/manifest/index.ts +1 -0
- package/src/manifest/manifest.test.ts +208 -90
- package/src/manifest/manifest.ts +34 -0
- package/src/modules/bridge.ts +8 -1
- package/src/modules/bridgeModule.ts +13 -1
- package/src/net/loopback.test.ts +10 -10
- package/src/panel/bridgeDetail.ts +2 -1
- package/src/presence/rosterSync.test.ts +7 -7
- package/src/probes/a2s.test.ts +5 -5
- package/src/probes/minecraftPing.test.ts +17 -17
- package/src/probes/raknetPing.test.ts +13 -13
- package/src/protocol/bridgeActivity.ts +54 -0
- package/src/protocol/bridgeEvents.ts +30 -0
- package/src/protocol/bridgeFrame.ts +10 -1
- package/src/protocol/index.ts +1 -0
- package/src/rcon/rconAccess.test.ts +20 -20
- package/src/rcon/rconAccess.ts +13 -13
- package/src/rcon/rconClient.test.ts +49 -49
- package/src/rcon/rconFrame.test.ts +14 -14
- package/src/rcon/rconIds.test.ts +4 -4
- package/src/rcon/rconWriter.test.ts +6 -6
- package/src/runtime/bridgeActions.ts +19 -2
- package/src/runtime/bridgeActivity.test.ts +318 -0
- package/src/runtime/bridgeActivity.ts +268 -0
- package/src/runtime/bridgeBackupLease.test.ts +9 -9
- package/src/runtime/bridgeConfigReader.test.ts +5 -5
- package/src/runtime/bridgeConsoleHandler.test.ts +8 -8
- package/src/runtime/bridgeContext.test.ts +11 -5
- package/src/runtime/bridgeContext.ts +8 -1
- package/src/runtime/bridgeDriver.test.ts +114 -93
- package/src/runtime/bridgeDriver.ts +10 -7
- package/src/runtime/bridgeEvents.test.ts +15 -15
- package/src/runtime/bridgeFiles.test.ts +54 -54
- package/src/runtime/bridgeInstall.test.ts +10 -10
- package/src/runtime/bridgeLiveConfig.test.ts +6 -6
- package/src/runtime/bridgeLog.ts +10 -0
- package/src/runtime/bridgePending.test.ts +6 -6
- package/src/runtime/bridgeRedaction.test.ts +16 -16
- package/src/runtime/bridgeRefusalError.ts +13 -0
- package/src/runtime/bridgeRegistry.test.ts +1077 -34
- package/src/runtime/bridgeRegistry.ts +464 -65
- package/src/runtime/bridgeScopedContext.ts +55 -0
- package/src/runtime/bridgeServer.test.ts +424 -24
- package/src/runtime/bridgeServer.ts +125 -35
- package/src/runtime/bridgeSetup.test.ts +27 -27
- package/src/runtime/bridgeSetup.ts +1 -1
- package/src/runtime/bridgeSetupHandlers.test.ts +80 -17
- package/src/runtime/bridgeSetupHandlers.ts +45 -10
- package/src/runtime/bridgeSignal.test.ts +7 -7
- package/src/runtime/bridgeSupervisor.test.ts +42 -42
- package/src/runtime/serve.ts +45 -3
- package/src/steam/avatars.test.ts +13 -13
- package/src/steam/steamApp.test.ts +8 -8
- package/src/steam/steamcmd.test.ts +44 -13
- package/src/steam/steamcmd.ts +27 -4
- package/src/steam/steamcmdNarrator.test.ts +4 -4
- package/src/steam/workshop.test.ts +5 -5
- package/src/utils/changedSettingKeys.test.ts +65 -0
- package/src/utils/changedSettingKeys.ts +6 -0
- package/src/utils/describeExecFailure.test.ts +34 -0
- package/src/utils/describeExecFailure.ts +9 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/sameSettingValue.test.ts +35 -0
- package/src/utils/sameSettingValue.ts +40 -0
- package/src/utils/token.test.ts +7 -7
- package/src/wine/protonCommand.test.ts +5 -5
- package/src/wine/wineCommand.test.ts +6 -6
- package/src/wine/winePrefix.test.ts +6 -6
- package/src/wire/activity.test.ts +95 -0
- package/src/wire/activity.ts +41 -0
- package/src/wire/index.ts +1 -0
- package/src/wire/panel.test.ts +91 -25
- package/src/wire/panel.ts +11 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { BridgeKind, BridgeSetupStepKind, PortJoin, PortProtocol } from "../core";
|
|
3
3
|
import { GuideOpenTab } from "../guides";
|
|
4
4
|
import type { ManifestPort, ManifestPortActivation } from "../manifest";
|
|
@@ -275,23 +275,44 @@ const driverWithTabs = (tabs: number, sections: number): BridgeDriver => ({
|
|
|
275
275
|
});
|
|
276
276
|
|
|
277
277
|
describe("panel caps", () => {
|
|
278
|
-
|
|
278
|
+
it("rejects a collection row action named after a read action", () => {
|
|
279
|
+
const driver = driverWithTabs(1, 1);
|
|
280
|
+
|
|
281
|
+
driver.modules = {
|
|
282
|
+
...driver.modules,
|
|
283
|
+
worlds: {
|
|
284
|
+
kind: BridgeKind.Collection,
|
|
285
|
+
list: async () => [],
|
|
286
|
+
actions: {
|
|
287
|
+
preview: async () => {
|
|
288
|
+
return;
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
expect(() => validateDriver(driver)).toThrow(
|
|
295
|
+
'module "worlds" declares action "preview", which is reserved for reads',
|
|
296
|
+
);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("takes the tab and section counts the agent mirror accepts", () => {
|
|
279
300
|
expect(() => validateDriver(driverWithTabs(8, 8))).not.toThrow();
|
|
280
301
|
});
|
|
281
302
|
|
|
282
|
-
|
|
303
|
+
it("rejects a ninth tab the agent mirror would drop", () => {
|
|
283
304
|
expect(() => validateDriver(driverWithTabs(9, 1))).toThrow(
|
|
284
305
|
"the panel declares 9 tabs — the platform takes at most 8",
|
|
285
306
|
);
|
|
286
307
|
});
|
|
287
308
|
|
|
288
|
-
|
|
309
|
+
it("rejects a ninth section the agent mirror would drop", () => {
|
|
289
310
|
expect(() => validateDriver(driverWithTabs(1, 9))).toThrow(
|
|
290
311
|
'tab "tab-0" declares 9 sections — the platform takes at most 8',
|
|
291
312
|
);
|
|
292
313
|
});
|
|
293
314
|
|
|
294
|
-
|
|
315
|
+
it("rejects a form carrying more fields than the agent mirror accepts", () => {
|
|
295
316
|
const driver = driverWithTabs(1, 1);
|
|
296
317
|
const [tab] = driver.panel?.tabs ?? [];
|
|
297
318
|
const [section] = tab?.sections ?? [];
|
|
@@ -307,7 +328,7 @@ describe("panel caps", () => {
|
|
|
307
328
|
expect(() => validateDriver(driver)).toThrow("declares 33 fields — the platform takes at most 32");
|
|
308
329
|
});
|
|
309
330
|
|
|
310
|
-
|
|
331
|
+
it("rejects a table carrying more columns than the agent mirror accepts", () => {
|
|
311
332
|
const driver = driverWithTabs(1, 1);
|
|
312
333
|
const [tab] = driver.panel?.tabs ?? [];
|
|
313
334
|
|
|
@@ -339,7 +360,7 @@ describe("panel caps", () => {
|
|
|
339
360
|
expect(() => validateDriver(driver)).toThrow("declares 9 columns — the platform takes at most 8");
|
|
340
361
|
});
|
|
341
362
|
|
|
342
|
-
|
|
363
|
+
it("rejects a label longer than the agent mirror accepts", () => {
|
|
343
364
|
const driver = driverWithTabs(1, 1);
|
|
344
365
|
const [tab] = driver.panel?.tabs ?? [];
|
|
345
366
|
|
|
@@ -356,7 +377,7 @@ describe("panel caps", () => {
|
|
|
356
377
|
});
|
|
357
378
|
|
|
358
379
|
describe("actions sections", () => {
|
|
359
|
-
|
|
380
|
+
it("accepts an action the module implements", () => {
|
|
360
381
|
expect(() =>
|
|
361
382
|
validateDriver(
|
|
362
383
|
driverWithActions([
|
|
@@ -372,7 +393,7 @@ describe("actions sections", () => {
|
|
|
372
393
|
).not.toThrow();
|
|
373
394
|
});
|
|
374
395
|
|
|
375
|
-
|
|
396
|
+
it("rejects an action the module does not implement", () => {
|
|
376
397
|
expect(() =>
|
|
377
398
|
validateDriver(
|
|
378
399
|
driverWithActions([
|
|
@@ -385,11 +406,11 @@ describe("actions sections", () => {
|
|
|
385
406
|
).toThrow('declares action "shout", which module "broadcast" does not implement');
|
|
386
407
|
});
|
|
387
408
|
|
|
388
|
-
|
|
409
|
+
it("rejects a section with no actions", () => {
|
|
389
410
|
expect(() => validateDriver(driverWithActions([]))).toThrow("declares no actions");
|
|
390
411
|
});
|
|
391
412
|
|
|
392
|
-
|
|
413
|
+
it("rejects an action carrying more fields than one prompt fits", () => {
|
|
393
414
|
expect(() =>
|
|
394
415
|
validateDriver(
|
|
395
416
|
driverWithActions([
|
|
@@ -410,7 +431,7 @@ describe("actions sections", () => {
|
|
|
410
431
|
).toThrow('action "say" declares 6 fields — at most 5 fit one prompt');
|
|
411
432
|
});
|
|
412
433
|
|
|
413
|
-
|
|
434
|
+
it("rejects a section pointing at a module that is not an actions module", () => {
|
|
414
435
|
const driver = driverWithActions([
|
|
415
436
|
{
|
|
416
437
|
id: "say",
|
|
@@ -428,7 +449,7 @@ describe("actions sections", () => {
|
|
|
428
449
|
expect(() => validateDriver(driver)).toThrow('references "broadcast", which is not an actions module');
|
|
429
450
|
});
|
|
430
451
|
|
|
431
|
-
|
|
452
|
+
it("checks the fields a row action declares", () => {
|
|
432
453
|
const driver = driverWithActions([
|
|
433
454
|
{
|
|
434
455
|
id: "say",
|
|
@@ -489,7 +510,7 @@ describe("actions sections", () => {
|
|
|
489
510
|
});
|
|
490
511
|
|
|
491
512
|
describe("visibleWhen", () => {
|
|
492
|
-
|
|
513
|
+
it("accepts a rule that matches on values", () => {
|
|
493
514
|
expect(() =>
|
|
494
515
|
validateDriver(
|
|
495
516
|
driverWith({
|
|
@@ -502,7 +523,7 @@ describe("visibleWhen", () => {
|
|
|
502
523
|
).not.toThrow();
|
|
503
524
|
});
|
|
504
525
|
|
|
505
|
-
|
|
526
|
+
it("accepts a rule that matches on emptiness", () => {
|
|
506
527
|
expect(() =>
|
|
507
528
|
validateDriver(
|
|
508
529
|
driverWith({
|
|
@@ -522,7 +543,7 @@ describe("visibleWhen", () => {
|
|
|
522
543
|
).not.toThrow();
|
|
523
544
|
});
|
|
524
545
|
|
|
525
|
-
|
|
546
|
+
it("accepts a rule that narrows values inside a presence check", () => {
|
|
526
547
|
expect(() =>
|
|
527
548
|
validateDriver(
|
|
528
549
|
driverWith({
|
|
@@ -536,7 +557,7 @@ describe("visibleWhen", () => {
|
|
|
536
557
|
).not.toThrow();
|
|
537
558
|
});
|
|
538
559
|
|
|
539
|
-
|
|
560
|
+
it("rejects a rule that declares neither values nor empty", () => {
|
|
540
561
|
expect(() =>
|
|
541
562
|
validateDriver(
|
|
542
563
|
driverWith({
|
|
@@ -546,7 +567,7 @@ describe("visibleWhen", () => {
|
|
|
546
567
|
).toThrow('is visible when "MODPACK" matches nothing');
|
|
547
568
|
});
|
|
548
569
|
|
|
549
|
-
|
|
570
|
+
it("rejects an empty values list", () => {
|
|
550
571
|
expect(() =>
|
|
551
572
|
validateDriver(
|
|
552
573
|
driverWith({
|
|
@@ -557,7 +578,7 @@ describe("visibleWhen", () => {
|
|
|
557
578
|
).toThrow('is visible when "MODPACK" matches nothing');
|
|
558
579
|
});
|
|
559
580
|
|
|
560
|
-
|
|
581
|
+
it("checks a field rule as well as a section rule", () => {
|
|
561
582
|
const driver = driverWith({
|
|
562
583
|
variable: "MODPACK",
|
|
563
584
|
empty: true,
|
|
@@ -584,11 +605,11 @@ describe("visibleWhen", () => {
|
|
|
584
605
|
});
|
|
585
606
|
|
|
586
607
|
describe("form preview", () => {
|
|
587
|
-
|
|
608
|
+
it("accepts a variables form pointing at a preview module", () => {
|
|
588
609
|
expect(() => validateDriver(driverWithPreview())).not.toThrow();
|
|
589
610
|
});
|
|
590
611
|
|
|
591
|
-
|
|
612
|
+
it("rejects a preview ref to a module that is not a preview module", () => {
|
|
592
613
|
const driver = driverWithPreview();
|
|
593
614
|
|
|
594
615
|
driver.modules = {
|
|
@@ -601,7 +622,7 @@ describe("form preview", () => {
|
|
|
601
622
|
expect(() => validateDriver(driver)).toThrow('references "transition", which is not a preview module');
|
|
602
623
|
});
|
|
603
624
|
|
|
604
|
-
|
|
625
|
+
it("rejects a preview on a form that writes settings", () => {
|
|
605
626
|
const driver = driverWithPreview();
|
|
606
627
|
const section = formSectionOf(driver);
|
|
607
628
|
|
|
@@ -624,7 +645,7 @@ describe("form preview", () => {
|
|
|
624
645
|
});
|
|
625
646
|
|
|
626
647
|
describe("validatePortActivation", () => {
|
|
627
|
-
|
|
648
|
+
it("accepts a variable a variables form or a catalog section declares", () => {
|
|
628
649
|
expect(() =>
|
|
629
650
|
validatePortActivation(
|
|
630
651
|
[
|
|
@@ -646,7 +667,7 @@ describe("validatePortActivation", () => {
|
|
|
646
667
|
).not.toThrow();
|
|
647
668
|
});
|
|
648
669
|
|
|
649
|
-
|
|
670
|
+
it("rejects a variable nothing in the panel declares", () => {
|
|
650
671
|
expect(() =>
|
|
651
672
|
validatePortActivation(
|
|
652
673
|
[
|
|
@@ -783,11 +804,11 @@ const clearingFormOf = (driver: BridgeDriver) => {
|
|
|
783
804
|
};
|
|
784
805
|
|
|
785
806
|
describe("detail sections", () => {
|
|
786
|
-
|
|
807
|
+
it("accepts a detail section on a detail module", () => {
|
|
787
808
|
expect(() => validateDriver(withNoop(driverWithDetail()))).not.toThrow();
|
|
788
809
|
});
|
|
789
810
|
|
|
790
|
-
|
|
811
|
+
it("rejects a section pointing at a module that is not a detail module", () => {
|
|
791
812
|
const driver = withNoop(driverWithDetail());
|
|
792
813
|
|
|
793
814
|
driver.modules = {
|
|
@@ -801,7 +822,7 @@ describe("detail sections", () => {
|
|
|
801
822
|
expect(() => validateDriver(driver)).toThrow('references "modpackStatus", which is not a detail module');
|
|
802
823
|
});
|
|
803
824
|
|
|
804
|
-
|
|
825
|
+
it("rejects an action the module does not implement", () => {
|
|
805
826
|
const driver = withNoop(driverWithDetail());
|
|
806
827
|
|
|
807
828
|
detailSectionOf(driver).actions = [
|
|
@@ -816,7 +837,7 @@ describe("detail sections", () => {
|
|
|
816
837
|
);
|
|
817
838
|
});
|
|
818
839
|
|
|
819
|
-
|
|
840
|
+
it("rejects a download action, since a detail has no rows to serve", () => {
|
|
820
841
|
const driver = withNoop(driverWithDetail());
|
|
821
842
|
|
|
822
843
|
detailSectionOf(driver).actions = [
|
|
@@ -830,7 +851,7 @@ describe("detail sections", () => {
|
|
|
830
851
|
expect(() => validateDriver(driver)).toThrow('declares action "update" as a download');
|
|
831
852
|
});
|
|
832
853
|
|
|
833
|
-
|
|
854
|
+
it("rejects a related tab the panel does not declare", () => {
|
|
834
855
|
const driver = withNoop(driverWithDetail());
|
|
835
856
|
|
|
836
857
|
detailSectionOf(driver).related = {
|
|
@@ -841,7 +862,7 @@ describe("detail sections", () => {
|
|
|
841
862
|
expect(() => validateDriver(driver)).toThrow('relates to tab "worlds", which the panel does not declare');
|
|
842
863
|
});
|
|
843
864
|
|
|
844
|
-
|
|
865
|
+
it("counts a detail variable as declared for port activation", () => {
|
|
845
866
|
expect(() =>
|
|
846
867
|
validatePortActivation(
|
|
847
868
|
[
|
|
@@ -859,11 +880,11 @@ describe("detail sections", () => {
|
|
|
859
880
|
});
|
|
860
881
|
|
|
861
882
|
describe("form clears", () => {
|
|
862
|
-
|
|
883
|
+
it("accepts a clear of a variable a detail section declares", () => {
|
|
863
884
|
expect(() => validateDriver(withNoop(driverWithDetail()))).not.toThrow();
|
|
864
885
|
});
|
|
865
886
|
|
|
866
|
-
|
|
887
|
+
it("rejects a clear of a variable nothing declares", () => {
|
|
867
888
|
const driver = withNoop(driverWithDetail());
|
|
868
889
|
|
|
869
890
|
clearingFormOf(driver).clears = [
|
|
@@ -878,7 +899,7 @@ describe("form clears", () => {
|
|
|
878
899
|
);
|
|
879
900
|
});
|
|
880
901
|
|
|
881
|
-
|
|
902
|
+
it("rejects a clear of one of the form's own fields", () => {
|
|
882
903
|
const driver = withNoop(driverWithDetail());
|
|
883
904
|
|
|
884
905
|
clearingFormOf(driver).clears = [
|
|
@@ -891,7 +912,7 @@ describe("form clears", () => {
|
|
|
891
912
|
expect(() => validateDriver(driver)).toThrow('clears "MC_VERSION", which is one of its own fields');
|
|
892
913
|
});
|
|
893
914
|
|
|
894
|
-
|
|
915
|
+
it("rejects clears on a form that writes settings", () => {
|
|
895
916
|
const driver = withNoop(driverWithDetail());
|
|
896
917
|
const form = clearingFormOf(driver);
|
|
897
918
|
|
|
@@ -969,16 +990,16 @@ describe("table uploads", () => {
|
|
|
969
990
|
mode,
|
|
970
991
|
});
|
|
971
992
|
|
|
972
|
-
|
|
993
|
+
it("accepts an upload that declares no mode", () => {
|
|
973
994
|
expect(() => validateDriver(driverWithUpload(upload()))).not.toThrow();
|
|
974
995
|
});
|
|
975
996
|
|
|
976
|
-
|
|
997
|
+
it("accepts both declared upload modes", () => {
|
|
977
998
|
expect(() => validateDriver(driverWithUpload(upload(BridgeUploadMode.Archive)))).not.toThrow();
|
|
978
999
|
expect(() => validateDriver(driverWithUpload(upload(BridgeUploadMode.File)))).not.toThrow();
|
|
979
1000
|
});
|
|
980
1001
|
|
|
981
|
-
|
|
1002
|
+
it("rejects an upload mode the platform does not know", () => {
|
|
982
1003
|
const section = upload("stream" as BridgeUploadMode);
|
|
983
1004
|
|
|
984
1005
|
expect(() => validateDriver(driverWithUpload(section))).toThrow('declares an unknown upload mode "stream"');
|
|
@@ -1015,7 +1036,7 @@ describe("the pending module", () => {
|
|
|
1015
1036
|
...overrides,
|
|
1016
1037
|
});
|
|
1017
1038
|
|
|
1018
|
-
|
|
1039
|
+
it("accepts a driver that declares no pending module", () => {
|
|
1019
1040
|
expect(() =>
|
|
1020
1041
|
validateDriver({
|
|
1021
1042
|
install: {
|
|
@@ -1034,11 +1055,11 @@ describe("the pending module", () => {
|
|
|
1034
1055
|
).not.toThrow();
|
|
1035
1056
|
});
|
|
1036
1057
|
|
|
1037
|
-
|
|
1058
|
+
it("accepts a pending module declaring a staging path and one extension", () => {
|
|
1038
1059
|
expect(() => validateDriver(driverWithPending(pendingWith({})))).not.toThrow();
|
|
1039
1060
|
});
|
|
1040
1061
|
|
|
1041
|
-
|
|
1062
|
+
it("rejects a pending module carrying another kind", () => {
|
|
1042
1063
|
const pending = pendingWith({
|
|
1043
1064
|
kind: BridgeKind.Collection as BridgeKind.Pending,
|
|
1044
1065
|
});
|
|
@@ -1048,7 +1069,7 @@ describe("the pending module", () => {
|
|
|
1048
1069
|
);
|
|
1049
1070
|
});
|
|
1050
1071
|
|
|
1051
|
-
|
|
1072
|
+
it("rejects a pending module with no staging path", () => {
|
|
1052
1073
|
expect(() =>
|
|
1053
1074
|
validateDriver(
|
|
1054
1075
|
driverWithPending(
|
|
@@ -1060,7 +1081,7 @@ describe("the pending module", () => {
|
|
|
1060
1081
|
).toThrow("the pending module declares no staging path");
|
|
1061
1082
|
});
|
|
1062
1083
|
|
|
1063
|
-
|
|
1084
|
+
it("rejects a staging path longer than the platform takes", () => {
|
|
1064
1085
|
expect(() =>
|
|
1065
1086
|
validateDriver(
|
|
1066
1087
|
driverWithPending(
|
|
@@ -1072,7 +1093,7 @@ describe("the pending module", () => {
|
|
|
1072
1093
|
).toThrow("the pending module staging path is 129 characters — the platform takes at most 128");
|
|
1073
1094
|
});
|
|
1074
1095
|
|
|
1075
|
-
|
|
1096
|
+
it("rejects a pending module declaring no extensions", () => {
|
|
1076
1097
|
expect(() =>
|
|
1077
1098
|
validateDriver(
|
|
1078
1099
|
driverWithPending(
|
|
@@ -1084,7 +1105,7 @@ describe("the pending module", () => {
|
|
|
1084
1105
|
).toThrow("the pending module declares no extensions");
|
|
1085
1106
|
});
|
|
1086
1107
|
|
|
1087
|
-
|
|
1108
|
+
it("rejects more extensions than the platform takes", () => {
|
|
1088
1109
|
expect(() =>
|
|
1089
1110
|
validateDriver(
|
|
1090
1111
|
driverWithPending(
|
|
@@ -1098,7 +1119,7 @@ describe("the pending module", () => {
|
|
|
1098
1119
|
).toThrow("the pending module declares 9 extensions — the platform takes at most 8");
|
|
1099
1120
|
});
|
|
1100
1121
|
|
|
1101
|
-
|
|
1122
|
+
it("rejects an extension longer than the platform takes", () => {
|
|
1102
1123
|
expect(() =>
|
|
1103
1124
|
validateDriver(
|
|
1104
1125
|
driverWithPending(
|
|
@@ -1112,7 +1133,7 @@ describe("the pending module", () => {
|
|
|
1112
1133
|
).toThrow('the pending module extension "xxxxxxxxxxxxxxxxx" is 17 characters — the platform takes at most 16');
|
|
1113
1134
|
});
|
|
1114
1135
|
|
|
1115
|
-
|
|
1136
|
+
it("reserves the pending module id for the singleton", () => {
|
|
1116
1137
|
expect(() =>
|
|
1117
1138
|
validateDriver({
|
|
1118
1139
|
install: {
|
|
@@ -1224,7 +1245,7 @@ const driverWithFields = (fields: BridgeField[]): BridgeDriver => ({
|
|
|
1224
1245
|
});
|
|
1225
1246
|
|
|
1226
1247
|
describe("the setup singleton", () => {
|
|
1227
|
-
|
|
1248
|
+
it("takes a driver step with both locales", () => {
|
|
1228
1249
|
expect(() =>
|
|
1229
1250
|
validateDriver(
|
|
1230
1251
|
driverWithSetup(
|
|
@@ -1236,11 +1257,11 @@ describe("the setup singleton", () => {
|
|
|
1236
1257
|
).not.toThrow();
|
|
1237
1258
|
});
|
|
1238
1259
|
|
|
1239
|
-
|
|
1260
|
+
it("rejects a setup that declares no steps", () => {
|
|
1240
1261
|
expect(() => validateDriver(driverWithSetup(setupWith([])))).toThrow("the setup module declares no steps");
|
|
1241
1262
|
});
|
|
1242
1263
|
|
|
1243
|
-
|
|
1264
|
+
it("rejects more steps than the platform takes", () => {
|
|
1244
1265
|
expect(() =>
|
|
1245
1266
|
validateDriver(
|
|
1246
1267
|
driverWithSetup(
|
|
@@ -1254,7 +1275,7 @@ describe("the setup singleton", () => {
|
|
|
1254
1275
|
).toThrow("the setup module declares 9 steps — the platform takes at most 8");
|
|
1255
1276
|
});
|
|
1256
1277
|
|
|
1257
|
-
|
|
1278
|
+
it("rejects a step id that is not kebab-case", () => {
|
|
1258
1279
|
expect(() =>
|
|
1259
1280
|
validateDriver(
|
|
1260
1281
|
driverWithSetup(
|
|
@@ -1266,7 +1287,7 @@ describe("the setup singleton", () => {
|
|
|
1266
1287
|
).toThrow('the setup module step id "signIn" must be kebab-case');
|
|
1267
1288
|
});
|
|
1268
1289
|
|
|
1269
|
-
|
|
1290
|
+
it("rejects the same step id twice", () => {
|
|
1270
1291
|
expect(() =>
|
|
1271
1292
|
validateDriver(
|
|
1272
1293
|
driverWithSetup(
|
|
@@ -1279,7 +1300,7 @@ describe("the setup singleton", () => {
|
|
|
1279
1300
|
).toThrow('the setup module declares step "sign-in" more than once');
|
|
1280
1301
|
});
|
|
1281
1302
|
|
|
1282
|
-
|
|
1303
|
+
it("rejects a title that is missing a locale", () => {
|
|
1283
1304
|
expect(() =>
|
|
1284
1305
|
validateDriver(
|
|
1285
1306
|
driverWithSetup(
|
|
@@ -1298,7 +1319,7 @@ describe("the setup singleton", () => {
|
|
|
1298
1319
|
).toThrow('the setup module step "sign-in" title needs both arabic and english text');
|
|
1299
1320
|
});
|
|
1300
1321
|
|
|
1301
|
-
|
|
1322
|
+
it("takes a form step that names an existing form section and its own fields", () => {
|
|
1302
1323
|
expect(() =>
|
|
1303
1324
|
validateDriver(
|
|
1304
1325
|
driverWithSetup(
|
|
@@ -1319,7 +1340,7 @@ describe("the setup singleton", () => {
|
|
|
1319
1340
|
).not.toThrow();
|
|
1320
1341
|
});
|
|
1321
1342
|
|
|
1322
|
-
|
|
1343
|
+
it("rejects a form step that names a section the panel does not declare", () => {
|
|
1323
1344
|
expect(() =>
|
|
1324
1345
|
validateDriver(
|
|
1325
1346
|
driverWithSetup(
|
|
@@ -1339,7 +1360,7 @@ describe("the setup singleton", () => {
|
|
|
1339
1360
|
);
|
|
1340
1361
|
});
|
|
1341
1362
|
|
|
1342
|
-
|
|
1363
|
+
it("rejects a form step that names a field the section does not declare", () => {
|
|
1343
1364
|
expect(() =>
|
|
1344
1365
|
validateDriver(
|
|
1345
1366
|
driverWithSetup(
|
|
@@ -1360,7 +1381,7 @@ describe("the setup singleton", () => {
|
|
|
1360
1381
|
).toThrow('the setup module step "name" names field "PASSWORD", which section "server" does not declare');
|
|
1361
1382
|
});
|
|
1362
1383
|
|
|
1363
|
-
|
|
1384
|
+
it("takes an open step that names an existing panel tab", () => {
|
|
1364
1385
|
expect(() =>
|
|
1365
1386
|
validateDriver(
|
|
1366
1387
|
driverWithSetup(
|
|
@@ -1380,7 +1401,7 @@ describe("the setup singleton", () => {
|
|
|
1380
1401
|
).not.toThrow();
|
|
1381
1402
|
});
|
|
1382
1403
|
|
|
1383
|
-
|
|
1404
|
+
it("rejects an open step that names a panel tab the panel does not declare", () => {
|
|
1384
1405
|
expect(() =>
|
|
1385
1406
|
validateDriver(
|
|
1386
1407
|
driverWithSetup(
|
|
@@ -1400,7 +1421,7 @@ describe("the setup singleton", () => {
|
|
|
1400
1421
|
).toThrow('the setup module step "tune" opens panel tab "world", which the panel does not declare');
|
|
1401
1422
|
});
|
|
1402
1423
|
|
|
1403
|
-
|
|
1424
|
+
it("takes an open step that leaves the panel altogether", () => {
|
|
1404
1425
|
expect(() =>
|
|
1405
1426
|
validateDriver(
|
|
1406
1427
|
driverWithSetup(
|
|
@@ -1419,7 +1440,7 @@ describe("the setup singleton", () => {
|
|
|
1419
1440
|
).not.toThrow();
|
|
1420
1441
|
});
|
|
1421
1442
|
|
|
1422
|
-
|
|
1443
|
+
it("takes a form step over a secret field", () => {
|
|
1423
1444
|
expect(() =>
|
|
1424
1445
|
validateDriver(
|
|
1425
1446
|
driverWithSetup(
|
|
@@ -1440,7 +1461,7 @@ describe("the setup singleton", () => {
|
|
|
1440
1461
|
).not.toThrow();
|
|
1441
1462
|
});
|
|
1442
1463
|
|
|
1443
|
-
|
|
1464
|
+
it("rejects a driver step while the setup exports no submit", () => {
|
|
1444
1465
|
expect(() =>
|
|
1445
1466
|
validateDriver(
|
|
1446
1467
|
driverWithSetup(
|
|
@@ -1452,7 +1473,7 @@ describe("the setup singleton", () => {
|
|
|
1452
1473
|
).toThrow("the setup module declares a driver step but exports no submit");
|
|
1453
1474
|
});
|
|
1454
1475
|
|
|
1455
|
-
|
|
1476
|
+
it("takes a setup with no submit while every step is answered elsewhere", () => {
|
|
1456
1477
|
expect(() =>
|
|
1457
1478
|
validateDriver(
|
|
1458
1479
|
driverWithSetup(
|
|
@@ -1471,7 +1492,7 @@ describe("the setup singleton", () => {
|
|
|
1471
1492
|
).not.toThrow();
|
|
1472
1493
|
});
|
|
1473
1494
|
|
|
1474
|
-
|
|
1495
|
+
it("reserves the setup module id for the singleton", () => {
|
|
1475
1496
|
expect(() =>
|
|
1476
1497
|
validateDriver({
|
|
1477
1498
|
install: {
|
|
@@ -1498,7 +1519,7 @@ describe("the setup singleton", () => {
|
|
|
1498
1519
|
});
|
|
1499
1520
|
|
|
1500
1521
|
describe("field patterns", () => {
|
|
1501
|
-
|
|
1522
|
+
it("takes a pattern on a text field and on a secret one", () => {
|
|
1502
1523
|
expect(() =>
|
|
1503
1524
|
validateDriver(
|
|
1504
1525
|
driverWithFields([
|
|
@@ -1506,8 +1527,8 @@ describe("field patterns", () => {
|
|
|
1506
1527
|
key: "LICENSE_KEY",
|
|
1507
1528
|
control: BridgeControl.Secret,
|
|
1508
1529
|
label: text("Licence key"),
|
|
1509
|
-
pattern: "^
|
|
1510
|
-
patternHint: text("A licence key starts with
|
|
1530
|
+
pattern: "^key_\\w{1,60}_\\w{1,20}$",
|
|
1531
|
+
patternHint: text("A licence key starts with key_"),
|
|
1511
1532
|
},
|
|
1512
1533
|
{
|
|
1513
1534
|
key: "SERVER_NAME",
|
|
@@ -1520,7 +1541,7 @@ describe("field patterns", () => {
|
|
|
1520
1541
|
).not.toThrow();
|
|
1521
1542
|
});
|
|
1522
1543
|
|
|
1523
|
-
|
|
1544
|
+
it("rejects a pattern that is not a regular expression", () => {
|
|
1524
1545
|
expect(() =>
|
|
1525
1546
|
validateDriver(
|
|
1526
1547
|
driverWithFields([
|
|
@@ -1528,14 +1549,14 @@ describe("field patterns", () => {
|
|
|
1528
1549
|
key: "LICENSE_KEY",
|
|
1529
1550
|
control: BridgeControl.Text,
|
|
1530
1551
|
label: text("Licence key"),
|
|
1531
|
-
pattern: "^
|
|
1552
|
+
pattern: "^key_(",
|
|
1532
1553
|
},
|
|
1533
1554
|
]),
|
|
1534
1555
|
),
|
|
1535
|
-
).toThrow('declares pattern "^
|
|
1556
|
+
).toThrow('declares pattern "^key_(", which is not a valid regular expression');
|
|
1536
1557
|
});
|
|
1537
1558
|
|
|
1538
|
-
|
|
1559
|
+
it("takes an alternation whose repeats are all bounded", () => {
|
|
1539
1560
|
expect(() =>
|
|
1540
1561
|
validateDriver(
|
|
1541
1562
|
driverWithFields([
|
|
@@ -1543,14 +1564,14 @@ describe("field patterns", () => {
|
|
|
1543
1564
|
key: "LICENSE_KEY",
|
|
1544
1565
|
control: BridgeControl.Secret,
|
|
1545
1566
|
label: text("Licence key"),
|
|
1546
|
-
pattern: "^(?:
|
|
1567
|
+
pattern: "^(?:key_\\w{1,60}_\\w{1,20}|\\w{32})$",
|
|
1547
1568
|
},
|
|
1548
1569
|
]),
|
|
1549
1570
|
),
|
|
1550
1571
|
).not.toThrow();
|
|
1551
1572
|
});
|
|
1552
1573
|
|
|
1553
|
-
|
|
1574
|
+
it("rejects a pattern that repeats a group which already repeats", () => {
|
|
1554
1575
|
expect(() =>
|
|
1555
1576
|
validateDriver(
|
|
1556
1577
|
driverWithFields([
|
|
@@ -1565,7 +1586,7 @@ describe("field patterns", () => {
|
|
|
1565
1586
|
).toThrow('declares pattern "^(a+)+$", which repeats a group that already repeats');
|
|
1566
1587
|
});
|
|
1567
1588
|
|
|
1568
|
-
|
|
1589
|
+
it("rejects a starred group whose body repeats without a bound", () => {
|
|
1569
1590
|
expect(() =>
|
|
1570
1591
|
validateDriver(
|
|
1571
1592
|
driverWithFields([
|
|
@@ -1580,7 +1601,7 @@ describe("field patterns", () => {
|
|
|
1580
1601
|
).toThrow("repeats a group that already repeats");
|
|
1581
1602
|
});
|
|
1582
1603
|
|
|
1583
|
-
|
|
1604
|
+
it("rejects a pattern longer than the platform takes", () => {
|
|
1584
1605
|
expect(() =>
|
|
1585
1606
|
validateDriver(
|
|
1586
1607
|
driverWithFields([
|
|
@@ -1595,7 +1616,7 @@ describe("field patterns", () => {
|
|
|
1595
1616
|
).toThrow("pattern is 258 characters — the platform takes at most 256");
|
|
1596
1617
|
});
|
|
1597
1618
|
|
|
1598
|
-
|
|
1619
|
+
it("rejects a pattern hint with no pattern to explain", () => {
|
|
1599
1620
|
expect(() =>
|
|
1600
1621
|
validateDriver(
|
|
1601
1622
|
driverWithFields([
|
|
@@ -1603,14 +1624,14 @@ describe("field patterns", () => {
|
|
|
1603
1624
|
key: "LICENSE_KEY",
|
|
1604
1625
|
control: BridgeControl.Text,
|
|
1605
1626
|
label: text("Licence key"),
|
|
1606
|
-
patternHint: text("A licence key starts with
|
|
1627
|
+
patternHint: text("A licence key starts with key_"),
|
|
1607
1628
|
},
|
|
1608
1629
|
]),
|
|
1609
1630
|
),
|
|
1610
1631
|
).toThrow("declares a pattern hint with no pattern to explain");
|
|
1611
1632
|
});
|
|
1612
1633
|
|
|
1613
|
-
|
|
1634
|
+
it("rejects a pattern on a control that never matches one", () => {
|
|
1614
1635
|
expect(() =>
|
|
1615
1636
|
validateDriver(
|
|
1616
1637
|
driverWithFields([
|
|
@@ -1625,7 +1646,7 @@ describe("field patterns", () => {
|
|
|
1625
1646
|
).toThrow('declares a pattern on a "boolean" control — only text and secret controls match one');
|
|
1626
1647
|
});
|
|
1627
1648
|
|
|
1628
|
-
|
|
1649
|
+
it("rejects a secret control that offers options", () => {
|
|
1629
1650
|
expect(() =>
|
|
1630
1651
|
validateDriver(
|
|
1631
1652
|
driverWithFields([
|
|
@@ -1710,7 +1731,7 @@ describe("section placement", () => {
|
|
|
1710
1731
|
return driver;
|
|
1711
1732
|
};
|
|
1712
1733
|
|
|
1713
|
-
|
|
1734
|
+
it("accepts a table placed on the players page", () => {
|
|
1714
1735
|
expect(() =>
|
|
1715
1736
|
validateDriver(
|
|
1716
1737
|
driverWithRoster({
|
|
@@ -1720,19 +1741,19 @@ describe("section placement", () => {
|
|
|
1720
1741
|
).not.toThrow();
|
|
1721
1742
|
});
|
|
1722
1743
|
|
|
1723
|
-
|
|
1744
|
+
it("rejects a form placed on the players page", () => {
|
|
1724
1745
|
expect(() => validateDriver(placedForm(BridgePlace.Players))).toThrow(
|
|
1725
1746
|
'tab "version" section "version" places a form section on players, which that page cannot render',
|
|
1726
1747
|
);
|
|
1727
1748
|
});
|
|
1728
1749
|
|
|
1729
|
-
|
|
1750
|
+
it("rejects a form placed on the overview page", () => {
|
|
1730
1751
|
expect(() => validateDriver(placedForm(BridgePlace.Overview))).toThrow(
|
|
1731
1752
|
"places a form section on overview, which that page cannot render",
|
|
1732
1753
|
);
|
|
1733
1754
|
});
|
|
1734
1755
|
|
|
1735
|
-
|
|
1756
|
+
it("rejects a table placed on the overview page", () => {
|
|
1736
1757
|
expect(() =>
|
|
1737
1758
|
validateDriver(
|
|
1738
1759
|
driverWithRoster({
|
|
@@ -1742,7 +1763,7 @@ describe("section placement", () => {
|
|
|
1742
1763
|
).toThrow('tab "roster" section "online" places a table section on overview, which that page cannot render');
|
|
1743
1764
|
});
|
|
1744
1765
|
|
|
1745
|
-
|
|
1766
|
+
it("accepts an offline row action on a table", () => {
|
|
1746
1767
|
expect(() =>
|
|
1747
1768
|
validateDriver(
|
|
1748
1769
|
driverWithRoster({
|
|
@@ -1759,7 +1780,7 @@ describe("section placement", () => {
|
|
|
1759
1780
|
).not.toThrow();
|
|
1760
1781
|
});
|
|
1761
1782
|
|
|
1762
|
-
|
|
1783
|
+
it("rejects an offline action on an actions section", () => {
|
|
1763
1784
|
expect(() =>
|
|
1764
1785
|
validateDriver(
|
|
1765
1786
|
driverWithActions([
|
|
@@ -1775,7 +1796,7 @@ describe("section placement", () => {
|
|
|
1775
1796
|
);
|
|
1776
1797
|
});
|
|
1777
1798
|
|
|
1778
|
-
|
|
1799
|
+
it("rejects a form help that is missing a locale", () => {
|
|
1779
1800
|
const driver = driverWith(anyVisibleWhen);
|
|
1780
1801
|
|
|
1781
1802
|
formSectionOf(driver).help = {
|
|
@@ -1806,7 +1827,7 @@ describe("the lifecycle module", () => {
|
|
|
1806
1827
|
},
|
|
1807
1828
|
});
|
|
1808
1829
|
|
|
1809
|
-
|
|
1830
|
+
it("accepts an environment resolver", () => {
|
|
1810
1831
|
expect(() =>
|
|
1811
1832
|
validateDriver(
|
|
1812
1833
|
lifecycleDriver({
|
|
@@ -1818,11 +1839,11 @@ describe("the lifecycle module", () => {
|
|
|
1818
1839
|
).not.toThrow();
|
|
1819
1840
|
});
|
|
1820
1841
|
|
|
1821
|
-
|
|
1842
|
+
it("accepts a lifecycle that declares no environment", () => {
|
|
1822
1843
|
expect(() => validateDriver(lifecycleDriver({}))).not.toThrow();
|
|
1823
1844
|
});
|
|
1824
1845
|
|
|
1825
|
-
|
|
1846
|
+
it("accepts each stop signal the bridge sends", () => {
|
|
1826
1847
|
for (const stopSignal of Object.values(BridgeStopSignal)) {
|
|
1827
1848
|
expect(() =>
|
|
1828
1849
|
validateDriver(
|
|
@@ -1834,7 +1855,7 @@ describe("the lifecycle module", () => {
|
|
|
1834
1855
|
}
|
|
1835
1856
|
});
|
|
1836
1857
|
|
|
1837
|
-
|
|
1858
|
+
it("refuses a stop signal the bridge does not send", () => {
|
|
1838
1859
|
expect(() =>
|
|
1839
1860
|
validateDriver(
|
|
1840
1861
|
lifecycleDriver({
|
|
@@ -1844,7 +1865,7 @@ describe("the lifecycle module", () => {
|
|
|
1844
1865
|
).toThrow('the lifecycle module declares stop signal "SIGHUP" — the bridge sends only SIGTERM or SIGINT');
|
|
1845
1866
|
});
|
|
1846
1867
|
|
|
1847
|
-
|
|
1868
|
+
it("accepts a ready timeout from one second to an hour", () => {
|
|
1848
1869
|
for (const readyTimeoutSeconds of [
|
|
1849
1870
|
1,
|
|
1850
1871
|
900,
|
|
@@ -1860,7 +1881,7 @@ describe("the lifecycle module", () => {
|
|
|
1860
1881
|
}
|
|
1861
1882
|
});
|
|
1862
1883
|
|
|
1863
|
-
|
|
1884
|
+
it("refuses a ready timeout outside one second to an hour", () => {
|
|
1864
1885
|
for (const readyTimeoutSeconds of [
|
|
1865
1886
|
0,
|
|
1866
1887
|
3601,
|
|
@@ -1878,7 +1899,7 @@ describe("the lifecycle module", () => {
|
|
|
1878
1899
|
}
|
|
1879
1900
|
});
|
|
1880
1901
|
|
|
1881
|
-
|
|
1902
|
+
it("accepts each lifecycle subject the bridge names", () => {
|
|
1882
1903
|
for (const subject of Object.values(BridgeLifecycleSubject)) {
|
|
1883
1904
|
expect(() =>
|
|
1884
1905
|
validateDriver(
|
|
@@ -1890,7 +1911,7 @@ describe("the lifecycle module", () => {
|
|
|
1890
1911
|
}
|
|
1891
1912
|
});
|
|
1892
1913
|
|
|
1893
|
-
|
|
1914
|
+
it("refuses a lifecycle subject the bridge does not name", () => {
|
|
1894
1915
|
expect(() =>
|
|
1895
1916
|
validateDriver(
|
|
1896
1917
|
lifecycleDriver({
|
|
@@ -1900,7 +1921,7 @@ describe("the lifecycle module", () => {
|
|
|
1900
1921
|
).toThrow('the lifecycle module declares subject "server" — the bridge names only game or bot');
|
|
1901
1922
|
});
|
|
1902
1923
|
|
|
1903
|
-
|
|
1924
|
+
it("refuses an environment that is not a function", () => {
|
|
1904
1925
|
expect(() =>
|
|
1905
1926
|
validateDriver(
|
|
1906
1927
|
lifecycleDriver({
|