@bongos/core 1.21.29 → 1.21.31
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/.bongos-core.json +70 -65
- package/docs/copy-inventory.md +374 -389
- package/docs/copy-registry.json +472 -637
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-readings.json +873 -883
- package/modules/hall-ui/public/atlas.html +3 -3
- package/modules/hall-ui/public/blockers.html +3 -3
- package/modules/hall-ui/public/board-room.html +3 -3
- package/modules/hall-ui/public/city.html +3 -3
- package/modules/hall-ui/public/collab.html +3 -3
- package/modules/hall-ui/public/commands.html +3 -3
- package/modules/hall-ui/public/copy-desk.html +3 -3
- package/modules/hall-ui/public/craft-mode.js +57 -3
- package/modules/hall-ui/public/deploy.html +3 -3
- package/modules/hall-ui/public/diagrams.html +3 -3
- package/modules/hall-ui/public/drachmae.html +3 -3
- package/modules/hall-ui/public/fleet.html +3 -3
- package/modules/hall-ui/public/gate.html +3 -3
- package/modules/hall-ui/public/goal-map.html +3 -3
- package/modules/hall-ui/public/goals.html +3 -3
- package/modules/hall-ui/public/government.html +3 -3
- package/modules/hall-ui/public/idea.html +3 -3
- package/modules/hall-ui/public/ideas.html +3 -3
- package/modules/hall-ui/public/index.html +3 -3
- package/modules/hall-ui/public/modules.html +3 -3
- package/modules/hall-ui/public/primer.html +3 -3
- package/modules/hall-ui/public/profile.html +3 -3
- package/modules/hall-ui/public/project-settings.html +3 -3
- package/modules/hall-ui/public/ranks.html +3 -3
- package/modules/hall-ui/public/roadmap.html +3 -3
- package/modules/hall-ui/public/roster.html +3 -3
- package/modules/hall-ui/public/sessions.html +3 -3
- package/modules/hall-ui/public/settings.html +3 -3
- package/modules/hall-ui/public/shell.js +2 -0
- package/modules/hall-ui/public/street.html +3 -3
- package/modules/hall-ui/public/studio.html +3 -3
- package/modules/hall-ui/public/style.css +2 -0
- package/modules/hall-ui/public/task.html +3 -3
- package/modules/hall-ui/public/thinking.html +3 -3
- package/modules/hall-ui/public/tweak-editor.html +3 -3
- package/modules/hall-ui/public/watch.html +3 -3
- package/modules/hall-ui/public/work.html +3 -3
- package/modules/hall-ui/records/craft-mode.md +7 -0
- package/modules/public-landing/public/projects-hall-live.states.json +1 -41
- package/modules/public-landing/public/projects.html +109 -72
- package/modules/public-landing/public/projects.probes.json +5 -5
- package/modules/public-landing/public/projects.states.json +7 -7
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/src/module-api.js +1 -1
- package/tests/craft_mode_nav.mjs +202 -0
- package/tests/hall_tweak_editor.mjs +1 -1
- package/tests/nav_permission_atoms.mjs +1 -1
- package/tests/projects_hub.mjs +7 -4
- package/tests/projects_hub_app_step.mjs +4 -3
- package/tests/projects_hub_module_picker.mjs +12 -10
- package/tests/projects_hub_pre_uat.mjs +1 -1
- package/tests/wizard_physics_more.mjs +58 -34
- package/tests/wizard_six_screens.mjs +10 -8
|
@@ -41,24 +41,24 @@
|
|
|
41
41
|
"in-new-terms-refused": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["click","#termsAck"],["wait",200]],"expect":{"visible":["#panel-fork"],"hidden":["#panel-what"],"attr":[["#wizStart","disabled",""],["#wizDemo","disabled",""]]}},
|
|
42
42
|
"in-new-fork-demo": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizDemo"],["wait",300]],"expect":{"visible":["#panel-fork","#demoSetup","#demoFitVerdict","#demoInvites","#wizBack"],"hidden":["#forkIntro"],"attr":[["#wizDemo","aria-expanded","true"]]}},
|
|
43
43
|
"in-new-demo-what": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizDemo"],["wait",300],["fill","#demoIdea","A voting app for movie night"],["click","#demoStart"],["wait",400]],"expect":{"visible":["#panel-what","#demoTag"],"hidden":["#panel-fork"],"text":[["#wizEyebrow","New demo"]]}},
|
|
44
|
-
"in-new-demo-review-address": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizDemo"],["wait",300],["fill","#demoIdea","A voting app for movie night"],["click","#demoStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-what button[type=submit]"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",300],["fill","#f-slug","mercury"],["wait",900]],"expect":{"visible":["#panel-review","#reviewRows .setRow[data-row=\"address\"] .opts","#f-slug","#addrConfirm","#f-noaddr"],"hidden":["#panel-checks"],"
|
|
44
|
+
"in-new-demo-review-address": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizDemo"],["wait",300],["fill","#demoIdea","A voting app for movie night"],["click","#demoStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-what button[type=submit]"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",300],["fill","#f-slug","mercury"],["wait",900]],"expect":{"visible":["#panel-review","#reviewRows .setRow[data-row=\"address\"] .opts","#f-slug","#addrConfirm","#f-noaddr"],"hidden":["#panel-checks"],"text":[["#wizEyebrow","New demo"]]}},
|
|
45
45
|
"in-new-what": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400]],"expect":{"visible":["#panel-what","#newName","#typeCards","#f-desc","#wizBack"],"hidden":["#panel-fork","#demoTag"],"attr":[["#wizRoom","data-screen","what"]]}},
|
|
46
46
|
"in-new-name-prefilled": {"auth":true,"url":"/projects?view=new&name=Mercury","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400]],"expect":{"visible":["#panel-what","#newName"]}},
|
|
47
47
|
"in-new-checks": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400]],"expect":{"visible":["#panel-checks","#physCountry","#physIntent"],"hidden":["#physAges","#physForms","#checksStop"],"attr":[["#wizRoom","data-screen","checks"]]}},
|
|
48
48
|
"in-new-checks-filled": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["select","#physRegion","US-CA"],["click","[data-phys=\"builder_geography\"][data-val=\"us-eu\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"true\"]"],["wait",200]],"expect":{"visible":["#physRegionField","#physAges"]}},
|
|
49
49
|
"in-new-checks-entity": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["click","#physHaveEntity"],["wait",200],["click","[data-phys=\"entity\"][data-val=\"for-profit\"]"],["wait",100]],"expect":{"visible":["#physForms","#physNoEntity"],"hidden":["#physIntent"]}},
|
|
50
|
-
"in-new-review": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700]],"expect":{"visible":["#panel-review","#reviewRows .setRow[data-row=\"code\"] [data-row-
|
|
50
|
+
"in-new-review": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700]],"expect":{"visible":["#panel-review","#reviewRows .setRow[data-row=\"code\"] [data-row-chg]","#reviewGo"],"hidden":["#panel-checks","#reviewRows .setRow[data-row=\"rewards\"]"],"text":[["#reviewGo","Looks right, continue"]],"attr":[["#wizRoom","data-screen","review"]]}},
|
|
51
51
|
"in-new-review-code": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-chg]"],["wait",300],["click","#c-adopt"],["wait",400]],"expect":{"visible":["#reviewRows .setRow[data-row=\"code\"] .opts","#adoptNote","#ghAdopt"]}},
|
|
52
52
|
"in-new-review-address": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",300],["fill","#f-slug","mercury"],["wait",900]],"expect":{"visible":["#f-slug","#addrConfirm","#f-noaddr"]}},
|
|
53
53
|
"in-new-review-app": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-chg]"],["wait",300],["click","#appOptRender input"],["wait",300]],"expect":{"visible":["#appOpts","#renderLater"]}},
|
|
54
54
|
"in-new-review-extras": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-chg]"],["wait",900]],"expect":{"visible":["#modQ","#modCards .tpill","#modCoreNote"]}},
|
|
55
55
|
"in-new-review-team": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-chg]"],["wait",300],["click","[data-phys-solo=\"many\"]"],["wait",600]],"expect":{"visible":["#physTeam","#physGuessLine","#teamCards","#reviewRows .setRow[data-row=\"rewards\"]"]}},
|
|
56
56
|
"in-new-review-run": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-chg]"],["wait",300],["click","[data-phys=\"framing\"][data-val=\"hybrid\"]"],["wait",600]],"expect":{"visible":["#physSuite"]}},
|
|
57
|
-
"in-new-review-confirmed": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"
|
|
58
|
-
"in-new-look": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"
|
|
59
|
-
"in-new-done": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"
|
|
60
|
-
"in-new-resume-offer": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"
|
|
61
|
-
"in-new-look-custom": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"
|
|
57
|
+
"in-new-review-confirmed": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200]],"expect":{"visible":["#reviewGo","#reviewRows .setRow.done"],"text":[["#reviewGo","Looks right, continue"]]}},
|
|
58
|
+
"in-new-look": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500]],"expect":{"visible":["#panel-look","#wizLook","#wizRecap","#doCreate"],"hidden":["#panel-review"],"attr":[["#wizRoom","data-screen","look"]]}},
|
|
59
|
+
"in-new-done": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500]],"expect":{"visible":["#panel-done","#liveProgress .prog","#humanSteps li","#wizInviteLater"],"hidden":["#panel-look","#qWrap","#wizBack","#wizInvite"]}},
|
|
60
|
+
"in-new-resume-offer": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500],["click","#projTrig"],["wait",350],["click","#menuProjects [data-nav=\"new\"]"],["wait",500]],"expect":{"visible":["#panel-fork","#wizResume","#wizResumeGo"],"hidden":["#panel-done"],"text":[["#wizResumeName","Mercury"]]}},
|
|
61
|
+
"in-new-look-custom": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#wizLook-custom"],["wait",200]],"expect":{"visible":["#panel-look","#wizAccent","#wizAccentNote"],"hidden":["#panel-review"]}},
|
|
62
62
|
"out-new": {"auth":false,"url":"/projects?view=new","expect":{"visible":["#panel-gate","#wizSignin"],"hidden":["#qWrap","#panel-fork"]}},
|
|
63
63
|
"out-map-projects": { "auth": false, "actions": [["click", "#projTrig"], ["wait", 350]], "expect": { "expanded": true, "hidden": ["#menuProjects [data-nav=\"mine\"]"] } },
|
|
64
64
|
"card": { "auth": true, "actions": [["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://example.com\"]"], ["wait", 900]], "expect": { "dialog": true, "hasClass": [["#projCard", "has-art"]], "visible": ["#cardArt", "#cardFaces", "#cardBuildersFig", "#cardShippedFig", "#cardOpen", "#cardHall"], "text": [["#cardBuilders", "11"], ["#cardShipped", "0"], ["#cardRel", "you build here"], ["#cardRuns", "example.com"], ["#cardFaces .facesMore", "+6"]], "count": [["#cardFaces img", 5]], "href": [["#cardHall", "https://builders.example.com/"]] } },
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.31",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.31",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.31",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -8930,5 +8930,17 @@
|
|
|
8930
8930
|
"id": "1004548",
|
|
8931
8931
|
"text": "After approving GitHub sign-in, founders go straight back to their setup checklist to wait, and can tick a box to have their project open automatically when it's ready."
|
|
8932
8932
|
}
|
|
8933
|
+
],
|
|
8934
|
+
"1.21.30": [
|
|
8935
|
+
{
|
|
8936
|
+
"id": "1004493",
|
|
8937
|
+
"text": "The hall's sidebar now follows the mode you pick. Building shows the everyday tabs, Artistry shows Studio, Goals and Your thinking, Ideating shows the Sky, Your thinking and Goals, and Governing is the shortest list: Blockers,"
|
|
8938
|
+
}
|
|
8939
|
+
],
|
|
8940
|
+
"1.21.31": [
|
|
8941
|
+
{
|
|
8942
|
+
"id": "1004550",
|
|
8943
|
+
"text": "Setting up a new project is quicker: the final check screen now takes one click to accept, with less text and no per-row confirm buttons."
|
|
8944
|
+
}
|
|
8933
8945
|
]
|
|
8934
8946
|
}
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.31'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// tests/craft_mode_nav.mjs — the hall nav follows the craft mode (task 1004493,
|
|
2
|
+
// goal 1000095 WA6).
|
|
3
|
+
//
|
|
4
|
+
// Each craft mode shows only the tabs its craft needs; Legacy mode and no mode
|
|
5
|
+
// show every tab, exactly as before. The owner's rule (2026-10-01): some tabs
|
|
6
|
+
// stay in EVERY mode, Builders first among them, written down as ONE shared list
|
|
7
|
+
// that each mode's list adds to. These cases boot the REAL shell-trail.js +
|
|
8
|
+
// craft-mode.js + shell.js and read the sidebar the shell actually rendered, so a
|
|
9
|
+
// tab id that drifts from NAV, or a gate pass that runs after the mode and
|
|
10
|
+
// re-shows a tab, reds here.
|
|
11
|
+
import assert from 'node:assert/strict';
|
|
12
|
+
import { test } from 'node:test';
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import vm from 'node:vm';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
|
|
18
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
19
|
+
const HALL = path.join(ROOT, 'modules/hall-ui/public');
|
|
20
|
+
const read = (f) => fs.readFileSync(path.join(HALL, f), 'utf8');
|
|
21
|
+
const SHELL = read('shell.js');
|
|
22
|
+
const TRAIL = read('shell-trail.js');
|
|
23
|
+
const CRAFT = read('craft-mode.js');
|
|
24
|
+
|
|
25
|
+
// Every NAV id, and the Government group's, read from shell.js itself.
|
|
26
|
+
const NAV_IDS = [...SHELL.matchAll(/\{ id: '([a-z-]+)', label:/g)].map((m) => m[1]);
|
|
27
|
+
const GOV_BLOCK = SHELL.slice(SHELL.indexOf("{ group: 'Government'"));
|
|
28
|
+
const GOV_IDS = [...GOV_BLOCK.slice(0, GOV_BLOCK.indexOf('] }')).matchAll(/\{ id: '([a-z-]+)'/g)].map((m) => m[1]);
|
|
29
|
+
|
|
30
|
+
// A small element. Elements parsed from the sidebar's markup are built ONCE, so
|
|
31
|
+
// what the gate loop writes (hidden) is what the mode pass reads back.
|
|
32
|
+
function makeEl(tag) {
|
|
33
|
+
const attrs = {};
|
|
34
|
+
return {
|
|
35
|
+
tagName: tag, id: '', dataset: {}, style: {}, hidden: false, children: [], _html: '',
|
|
36
|
+
classList: { _s: new Set(), add(c) { this._s.add(c); }, remove(c) { this._s.delete(c); }, toggle(c, on) { if (on === undefined) on = !this._s.has(c); if (on) this._s.add(c); else this._s.delete(c); return on; }, contains(c) { return this._s.has(c); } },
|
|
37
|
+
setAttribute(k, v) { attrs[k] = String(v); }, getAttribute(k) { return k in attrs ? attrs[k] : null; },
|
|
38
|
+
hasAttribute(k) { return k in attrs; }, removeAttribute(k) { delete attrs[k]; },
|
|
39
|
+
addEventListener() {}, appendChild(c) { this.children.push(c); return c; }, removeChild() {}, insertBefore(c) { return c; },
|
|
40
|
+
focus() {}, closest() { return null; }, querySelector() { return null; }, querySelectorAll() { return []; },
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// The rendered sidebar as groups of { head, items }.
|
|
44
|
+
function parseSidebar(html) {
|
|
45
|
+
return html.split('<div class="nav-group').slice(1).map((chunk) => {
|
|
46
|
+
const group = makeEl('DIV');
|
|
47
|
+
const tag = (m, t) => {
|
|
48
|
+
const el = makeEl(t);
|
|
49
|
+
for (const a of m.matchAll(/([a-z-]+)="([^"]*)"/g)) {
|
|
50
|
+
if (a[1] === 'id') el.id = a[2];
|
|
51
|
+
else if (a[1].startsWith('data-')) el.dataset[a[1].slice(5)] = a[2];
|
|
52
|
+
}
|
|
53
|
+
el.hidden = /\shidden[\s>]/.test(m);
|
|
54
|
+
return el;
|
|
55
|
+
};
|
|
56
|
+
const headM = chunk.match(/<button type="button" class="nav-group__head"[^>]*>/);
|
|
57
|
+
const head = headM ? tag(headM[0], 'BUTTON') : null;
|
|
58
|
+
const items = [...chunk.matchAll(/<a class="nav-item"[^>]*>/g)].map((m) => tag(m[0], 'A'));
|
|
59
|
+
group.querySelector = (sel) => (sel === '.nav-group__head' ? head : null);
|
|
60
|
+
group.querySelectorAll = (sel) => (sel === '.nav-item' ? items : []);
|
|
61
|
+
return { el: group, head, items };
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function boot({ session = {}, perms = { 'board.vote.cast': true } } = {}) {
|
|
66
|
+
const root = makeEl('HTML');
|
|
67
|
+
const topbar = makeEl('DIV');
|
|
68
|
+
const sidebar = makeEl('NAV');
|
|
69
|
+
const body = makeEl('BODY');
|
|
70
|
+
body.dataset.page = 'work';
|
|
71
|
+
let groups = null;
|
|
72
|
+
const parsed = () => (groups ||= parseSidebar(sidebar._html));
|
|
73
|
+
Object.defineProperty(sidebar, 'innerHTML', { set(v) { sidebar._html = String(v); groups = null; }, get() { return sidebar._html; } });
|
|
74
|
+
sidebar.querySelectorAll = (sel) => {
|
|
75
|
+
if (sel === '.nav-item') return parsed().flatMap((g) => g.items);
|
|
76
|
+
if (sel === '.nav-group') return parsed().map((g) => g.el);
|
|
77
|
+
return [];
|
|
78
|
+
};
|
|
79
|
+
const gated = () => parsed().flatMap((g) => (g.head ? [g.head, ...g.items] : g.items));
|
|
80
|
+
const els = { 'app-topbar': topbar, 'app-sidebar': sidebar };
|
|
81
|
+
const documentObj = {
|
|
82
|
+
body, referrer: '', documentElement: root, activeElement: null,
|
|
83
|
+
getElementById: (id) => els[id] || null,
|
|
84
|
+
querySelector: () => null,
|
|
85
|
+
querySelectorAll: (sel) => (sel === '#app-sidebar [data-gate]' ? gated() : []),
|
|
86
|
+
createElement: (t) => makeEl(t.toUpperCase()),
|
|
87
|
+
addEventListener() {}, removeEventListener() {}, dispatchEvent() { return true; },
|
|
88
|
+
get cookie() { return ''; }, set cookie(_v) {},
|
|
89
|
+
};
|
|
90
|
+
const location = { hostname: 'h', origin: 'https://h', href: 'https://h/work', pathname: '/work', hash: '', search: '', assign() {}, replace() {}, reload() {} };
|
|
91
|
+
const mk = (obj) => ({ getItem: (k) => (k in obj ? obj[k] : null), setItem: (k, v) => { obj[k] = String(v); }, removeItem: (k) => { delete obj[k]; } });
|
|
92
|
+
const windowObj = {
|
|
93
|
+
__BRANDING__: {}, __MODULES__: { enabled: { provisioning: true } },
|
|
94
|
+
BongosClient: { createClient: () => ({ request: () => new Promise(() => {}) }) },
|
|
95
|
+
matchMedia: () => ({ matches: false, addEventListener() {} }),
|
|
96
|
+
localStorage: mk({}), sessionStorage: mk(session), location,
|
|
97
|
+
clearTimeout() {}, setTimeout() { return 0; }, requestAnimationFrame() {}, scrollTo() {},
|
|
98
|
+
addEventListener() {}, dispatchEvent() { return true; },
|
|
99
|
+
};
|
|
100
|
+
const sandbox = {
|
|
101
|
+
window: windowObj, document: documentObj, location, console, URL, URLSearchParams, queueMicrotask,
|
|
102
|
+
setTimeout() { return 0; }, clearTimeout() {},
|
|
103
|
+
Event: class { constructor(t) { this.type = t; } },
|
|
104
|
+
CustomEvent: class { constructor(t, i) { this.type = t; Object.assign(this, i); } },
|
|
105
|
+
};
|
|
106
|
+
sandbox.globalThis = sandbox;
|
|
107
|
+
vm.createContext(sandbox);
|
|
108
|
+
vm.runInContext(TRAIL, sandbox, { filename: 'shell-trail.js' });
|
|
109
|
+
vm.runInContext(CRAFT, sandbox, { filename: 'craft-mode.js' });
|
|
110
|
+
vm.runInContext(SHELL, sandbox, { filename: 'shell.js' });
|
|
111
|
+
const shell = windowObj.OTBShell;
|
|
112
|
+
const craft = windowObj.OTBCraftMode;
|
|
113
|
+
const byId = (id) => parsed().flatMap((g) => g.items).find((a) => a.id === 'nav-' + id);
|
|
114
|
+
return {
|
|
115
|
+
craft, root,
|
|
116
|
+
signIn: (rank = 'archon') => shell.applyAccess({ authed: true, rank, permissions: perms, builder: { id: 7, rank } }),
|
|
117
|
+
// The tabs a reader can see: the gate let them through AND the mode kept them.
|
|
118
|
+
showing: () => parsed().flatMap((g) => g.items).filter((a) => !a.hidden && !a.hasAttribute('data-craft-off')).map((a) => a.id.slice(4)),
|
|
119
|
+
headShowing: (group) => {
|
|
120
|
+
const g = parsed().find((x) => x.head && x.head.dataset.group === group);
|
|
121
|
+
return !!g && !g.head.hidden && !g.head.hasAttribute('data-craft-off');
|
|
122
|
+
},
|
|
123
|
+
byId,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const CRAFTS = ['building', 'artistry', 'ideating', 'governing'];
|
|
128
|
+
|
|
129
|
+
test('THE ALWAYS-PRESENT LIST: one shared list, Builders first, inside every mode', () => {
|
|
130
|
+
const { craft } = boot();
|
|
131
|
+
assert.equal(craft.ALWAYS_TABS[0], 'roster', 'Builders (NAV id roster) heads the list (owner, 2026-10-01)');
|
|
132
|
+
for (const m of CRAFTS) {
|
|
133
|
+
const tabs = craft.tabsFor(m);
|
|
134
|
+
assert.ok(Array.isArray(tabs), `${m} has a named tab list`);
|
|
135
|
+
for (const id of craft.ALWAYS_TABS) assert.ok(tabs.includes(id), `${m} keeps ${id}`);
|
|
136
|
+
}
|
|
137
|
+
assert.match(CRAFT, /return own \? ALWAYS_TABS\.concat\(own\) : null;/, 'each list is ALWAYS_TABS plus its own, not a copy of it');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('every tab a mode names is a real NAV item, and every NAV item lives in some mode', () => {
|
|
141
|
+
const { craft } = boot();
|
|
142
|
+
assert.ok(NAV_IDS.length >= 25, `read ${NAV_IDS.length} NAV ids from shell.js`);
|
|
143
|
+
const named = new Set(CRAFTS.flatMap((m) => craft.tabsFor(m)));
|
|
144
|
+
for (const id of named) assert.ok(NAV_IDS.includes(id), `${id} is a NAV id`);
|
|
145
|
+
for (const id of NAV_IDS) assert.ok(named.has(id), `${id} is reachable from some mode's nav`);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('Government shows only in Governing, and Governing is the shortest list', () => {
|
|
149
|
+
const { craft } = boot();
|
|
150
|
+
assert.ok(GOV_IDS.length >= 5, `read ${GOV_IDS.length} Government ids`);
|
|
151
|
+
for (const m of CRAFTS) {
|
|
152
|
+
for (const id of GOV_IDS) assert.equal(craft.tabsFor(m).includes(id), m === 'governing', `${id} in ${m}`);
|
|
153
|
+
}
|
|
154
|
+
const lengths = CRAFTS.map((m) => craft.tabsFor(m).filter((id) => !GOV_IDS.includes(id)).length);
|
|
155
|
+
assert.equal(Math.min(...lengths), lengths[CRAFTS.indexOf('governing')], 'outside its own Government group, Governing shows the fewest tabs');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('NO MODE AND LEGACY ARE TODAY\'S NAV: every tab the gate allows, nothing marked', () => {
|
|
159
|
+
const none = boot();
|
|
160
|
+
none.signIn();
|
|
161
|
+
const all = none.showing();
|
|
162
|
+
assert.deepEqual(all, NAV_IDS, 'an archon with no mode sees every tab');
|
|
163
|
+
const legacy = boot({ session: { 'hall-craft': 'legacy' } });
|
|
164
|
+
legacy.signIn();
|
|
165
|
+
assert.deepEqual(legacy.showing(), all, 'Legacy mode shows exactly the same');
|
|
166
|
+
assert.equal(legacy.craft.tabsFor('legacy'), null);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('each mode shows its own tabs, and switching updates the nav live', () => {
|
|
170
|
+
const p = boot();
|
|
171
|
+
p.signIn();
|
|
172
|
+
for (const m of CRAFTS) {
|
|
173
|
+
p.craft.choose(m);
|
|
174
|
+
assert.equal(p.root.dataset.craft, m);
|
|
175
|
+
assert.deepEqual(new Set(p.showing()), new Set(p.craft.tabsFor(m)), `${m} shows exactly its list`);
|
|
176
|
+
assert.ok(p.showing().includes('roster'), `Builders stays in ${m}`);
|
|
177
|
+
assert.equal(p.headShowing('government'), m === 'governing', `the Government head shows only where its tabs do (${m})`);
|
|
178
|
+
}
|
|
179
|
+
p.craft.choose('legacy');
|
|
180
|
+
assert.deepEqual(p.showing(), NAV_IDS, 'back to Legacy, every tab returns without a reload');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('a mode never shows a tab the gate hid, and a later gate pass keeps the mode', () => {
|
|
184
|
+
const p = boot({ session: { 'hall-craft': 'governing' } });
|
|
185
|
+
p.signIn('thetes');
|
|
186
|
+
assert.ok(!p.showing().includes('watch'), 'a thetes in Governing does not get the metic-floored Watch tab');
|
|
187
|
+
assert.ok(!p.headShowing('government'), 'nor its group head');
|
|
188
|
+
assert.deepEqual(p.showing(), ['blockers', 'board-room', 'roster'], 'what a thetes board member in Governing sees');
|
|
189
|
+
p.signIn('archon');
|
|
190
|
+
assert.ok(p.showing().includes('watch'), 'promoted, the gate lets it through and the mode keeps it');
|
|
191
|
+
assert.ok(p.headShowing('government'), 'and its group head comes back with it, which only a mode pass after the gate can know');
|
|
192
|
+
assert.ok(!p.showing().includes('work'), 'and the gate pass did not bring back a tab the mode left out');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('shell.js applies the mode AFTER its gate loop, and style.css hides what the mode leaves out', () => {
|
|
196
|
+
const body = SHELL.slice(SHELL.indexOf('function applyAccess('));
|
|
197
|
+
const gate = body.indexOf('el.hidden = !ok;');
|
|
198
|
+
const mode = body.indexOf('CRAFT.applyNav()');
|
|
199
|
+
assert.ok(gate > 0 && mode > gate, 'applyNav runs after the gate loop settles `hidden`');
|
|
200
|
+
assert.match(read('style.css'), /#app-sidebar \[data-craft-off\] \{ display: none !important; \}/);
|
|
201
|
+
assert.doesNotMatch(CRAFT, /\.hidden\s*=/, 'the mode never writes `hidden`, which the gate owns');
|
|
202
|
+
});
|
|
@@ -635,7 +635,7 @@ try {
|
|
|
635
635
|
assert.match(HTML, /<body data-page="tweak-editor"[^>]*data-fullscreen-switch/);
|
|
636
636
|
assert.equal((HTML.match(/<h1\b/g) || []).length, 1);
|
|
637
637
|
assert.ok(HTML.indexOf('tweak-editor-lib.js') < HTML.indexOf('tweak-editor.js?'), 'the lib loads first');
|
|
638
|
-
assert.match(HTML, /shell\.js\?v=2026-10-
|
|
638
|
+
assert.match(HTML, /shell\.js\?v=2026-10-03-nav/);
|
|
639
639
|
assert.equal(/fonts\.googleapis\.com\/css/.test(HTML), false);
|
|
640
640
|
for (const id of ['tw-bar', 'tw-counter', 'tw-open', 'tw-open-more', 'tw-divider', 'tw-openwrap', 'tw-open-menu', 'tw-open-tab', 'tw-open-split', 'tw-frame', 'tw-frame-view', 'tw-split-close', 'tw-download', 'tw-upload', 'tw-file', 'tw-submit', 'tw-paper', 'tw-doc', 'tw-notice', 'tw-done', 'tw-take', 'tw-pick', 'tw-picks', 'tw-refused', 'tw-refused-title', 'tw-refused-list', 'tw-refused-next', 'tw-refused-download', 'tw-refused-close']) {
|
|
641
641
|
assert.ok(new RegExp(`id="${id}"`).test(HTML), `#${id} is in the markup`);
|
|
@@ -87,7 +87,7 @@ test('the hall-preview fixture carries every atom /me resolves', () => {
|
|
|
87
87
|
// the file and left the query alone, so a returning browser kept the cached copy —
|
|
88
88
|
// a fix that reaches nobody looks identical to a fix that does not work.
|
|
89
89
|
|
|
90
|
-
const SHELL_SHA = '
|
|
90
|
+
const SHELL_SHA = 'af1c61ca87767b44';
|
|
91
91
|
|
|
92
92
|
test('shell.js has not changed without its cache-bust being bumped', () => {
|
|
93
93
|
const actual = crypto.createHash('sha256').update(SHELL).digest('hex').slice(0, 16);
|
package/tests/projects_hub.mjs
CHANGED
|
@@ -2316,8 +2316,11 @@ test('the extras picker ships as real markup on the review\'s Extras row and is
|
|
|
2316
2316
|
assert.match(EXTRAS_ROW, /id="modCards"/, 'the band the picker paints its rows into');
|
|
2317
2317
|
assert.match(EXTRAS_ROW, /class="typeCards"/, 'and it is the SAME band the type and team questions use — no new grammar');
|
|
2318
2318
|
assert.match(EXTRAS_ROW, /id="modCoreNote"/, 'the always-on core is stated on the row');
|
|
2319
|
-
// the suggested set is accepted by
|
|
2320
|
-
|
|
2319
|
+
// the suggested set is accepted by a control — the page's "Looks right, continue", or
|
|
2320
|
+
// Done on the open row (task 1004550) — never inferred
|
|
2321
|
+
assert.match(HUB, /<div class="setRow" data-row="extras">[\s\S]*?<button type="button" class="rowChg" data-row-chg aria-expanded="false">Edit<\/button>/,
|
|
2322
|
+
'the row can be opened and changed');
|
|
2323
|
+
assert.match(HUB, /<button type="submit" class="btn" id="reviewGo">Looks right, continue<\/button>/,
|
|
2321
2324
|
'accepting the suggested set is a control, not an inference');
|
|
2322
2325
|
// confirming turns the shown set into an answer given; the row's validity never refuses it
|
|
2323
2326
|
const confirm = (SCRIPT.match(/function confirmRow\(key\) \{([\s\S]*?)\n \}/) || [, ''])[1];
|
|
@@ -2397,6 +2400,6 @@ test('the kit stub answers the picker\'s read, so the panel renders in the desig
|
|
|
2397
2400
|
assert.match(stub, /rest === '\/provisioning\/starter-bundles'/, 'the stub serves the picker\'s read');
|
|
2398
2401
|
const probes = JSON.parse(fs.readFileSync(path.join(LANDING, 'projects.probes.json'), 'utf8')).probes;
|
|
2399
2402
|
assert.ok(probes.some((p) => /the wizard driver \(tasks? 1003346\b/.test(p.name)
|
|
2400
|
-
&& p.steps.some((st) => st[1] === '#
|
|
2401
|
-
'and the end-to-end driver confirms the
|
|
2403
|
+
&& p.steps.some((st) => st[1] === '#reviewGo')),
|
|
2404
|
+
'and the end-to-end driver confirms the review rather than stopping at it (tasks 1004504, 1004550)');
|
|
2402
2405
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// tests/projects_hub_app_step.mjs — the create wizard's "where your app runs" question,
|
|
2
2
|
// EXECUTED (task 1004351, ADR 0345), and its Render setup (task 1004352, ADR 0348). Since
|
|
3
3
|
// task 1004504 the old App step is the review's "App hosting" row: its controls moved into
|
|
4
|
-
// the row whole, and
|
|
4
|
+
// the row whole, and Done (the row's Edit, open) runs the check its Next used to.
|
|
5
5
|
//
|
|
6
6
|
// A static regex cannot tell a guard that works from one that is never consulted, so —
|
|
7
7
|
// as tests/projects_hub_module_picker.mjs does for the picker — this slices the block out
|
|
@@ -162,7 +162,7 @@ test('the row is real markup: Render and Decide later, Decide later the default'
|
|
|
162
162
|
assert.doesNotMatch(PANEL, /Coming soon/, 'and is no longer marked soon');
|
|
163
163
|
assert.match(PANEL, /<input type="radio" name="appPlatform" value="later" checked>/, 'Decide later is the default');
|
|
164
164
|
assert.ok(PANEL.indexOf('value="later"') < PANEL.indexOf('value="render"'), 'Decide later comes first');
|
|
165
|
-
assert.match(
|
|
165
|
+
assert.match(HUB.slice(HUB.indexOf('<div class="setRow" data-row="app">')), /^[\s\S]*?<span class="acts"><button type="button" class="rowChg" data-row-chg aria-expanded="false">Edit<\/button>/, 'Edit opens the row, and reads Done to confirm it (task 1004550)');
|
|
166
166
|
assert.doesNotMatch(HUB, /id="panel-app"/, 'the old App panel is gone');
|
|
167
167
|
});
|
|
168
168
|
|
|
@@ -230,7 +230,8 @@ test('the hall is spelled one way on this page', () => {
|
|
|
230
230
|
test('the key field says what the key is used for, and claims no protection it lacks', () => {
|
|
231
231
|
const setup = (PANEL.match(/<div class="renderSetup"[\s\S]*?<div class="hint" id="h-render"/) || [''])[0];
|
|
232
232
|
assert.match(setup, /<input type="password" id="f-renderKey" autocomplete="off"/, 'masked, and never autofilled');
|
|
233
|
-
assert.match(setup, /
|
|
233
|
+
assert.match(setup, /Used once to create your app on your Render account, then thrown away — not kept, not even in this browser/);
|
|
234
|
+
assert.match(setup, /A key can act on your whole Render account/, 'what a key can reach is still said (task 1004550 shortened the rest)');
|
|
234
235
|
assert.match(setup, /Render bills you directly/);
|
|
235
236
|
assert.match(setup, /href="https:\/\/dashboard\.render\.com\/u\/settings#api-keys"/, 'links to where Render issues keys');
|
|
236
237
|
assert.doesNotMatch(setup, /encrypt|secure|safe|protected/i, 'no protection claim the design does not give (ADR 0327 §2)');
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// tests/projects_hub_module_picker.mjs — the create wizard's module picker,
|
|
2
2
|
// EXECUTED (BV1.R20, task 1002339; goal 1000046 criterion `proj-module-select`).
|
|
3
3
|
// Since task 1004504 the picker is the review's Extras row: its controls moved into the
|
|
4
|
-
// row whole, "Looks right
|
|
5
|
-
// Create waits for every row,
|
|
4
|
+
// row whole, the page's one "Looks right, continue" (or Done on the open row) confirms
|
|
5
|
+
// the set on screen (task 1004550), and there is no Skip — Create waits for every row,
|
|
6
|
+
// this one included.
|
|
6
7
|
//
|
|
7
8
|
// tests/projects_hub.mjs pins this surface statically, on purpose (C7: "real
|
|
8
9
|
// markup, not JS-built"). But the picker is the one part of the wizard whose
|
|
@@ -146,11 +147,11 @@ function makeEl(id) {
|
|
|
146
147
|
};
|
|
147
148
|
}
|
|
148
149
|
|
|
149
|
-
// One review row, shaped like the markup: its hidden .opts
|
|
150
|
+
// One review row, shaped like the markup: its hidden .opts and its one Edit / Done control.
|
|
150
151
|
function makeRow(key) {
|
|
151
152
|
const row = makeEl('row-' + key);
|
|
152
153
|
row.setAttribute('data-row', key);
|
|
153
|
-
const parts = { '.opts': makeEl('opts-' + key), '[data-row-
|
|
154
|
+
const parts = { '.opts': makeEl('opts-' + key), '[data-row-chg]': makeEl('chg-' + key) };
|
|
154
155
|
parts['.opts'].hidden = true;
|
|
155
156
|
row.querySelector = (sel) => parts[sel] || null;
|
|
156
157
|
return row;
|
|
@@ -220,7 +221,7 @@ function boot({ state: seed = {}, fetchOk = true, payload = PAYLOAD } = {}) {
|
|
|
220
221
|
// a click inside the review, through its one delegated listener, as the browser sends it
|
|
221
222
|
const rowClick = (attr) => {
|
|
222
223
|
const row = rowFor('#reviewRows .setRow[data-row="extras"]');
|
|
223
|
-
const t = { hasAttribute: (a) => a === attr, closest: (sel) => (sel === '.setRow' ? row : null) };
|
|
224
|
+
const t = { hasAttribute: (a) => a === attr, closest: (sel) => (sel === '.setRow' ? row : null), focus() {} };
|
|
224
225
|
$('reviewRows')._handlers.click({ target: { closest: () => t } });
|
|
225
226
|
};
|
|
226
227
|
return {
|
|
@@ -246,10 +247,11 @@ function boot({ state: seed = {}, fetchOk = true, payload = PAYLOAD } = {}) {
|
|
|
246
247
|
};
|
|
247
248
|
band._handlers.click({ target });
|
|
248
249
|
},
|
|
249
|
-
// "Looks right"
|
|
250
|
-
|
|
250
|
+
// the page's "Looks right, continue" (every row through its own check), Edit to
|
|
251
|
+
// open the Extras row, and Done — the same control, open — to confirm it
|
|
252
|
+
looksRight: () => sandbox.confirmAllRows(),
|
|
251
253
|
change: () => rowClick('data-row-chg'),
|
|
252
|
-
use: () => rowClick('data-row-
|
|
254
|
+
use: () => rowClick('data-row-chg'),
|
|
253
255
|
confirmed: () => !!(state.confirmed && state.confirmed.extras),
|
|
254
256
|
unconfirmed: () => [...sandbox.unconfirmedRows()],
|
|
255
257
|
forCreate: () => sandbox.modulesForCreate(),
|
|
@@ -355,7 +357,7 @@ test('an unconfirmed row answers NOTHING, and Create waits for it (no Skip since
|
|
|
355
357
|
assert.ok(h.unconfirmed().includes('extras'), 'and the review holds Continue until the row is confirmed');
|
|
356
358
|
});
|
|
357
359
|
|
|
358
|
-
test('
|
|
360
|
+
test('Edit, a toggle, then Done: the toggle stands as the answer', async () => {
|
|
359
361
|
// A visitor who changed something and then confirmed meant "this", not "throw that away".
|
|
360
362
|
const h = boot({ state: { projType: 'game' } });
|
|
361
363
|
await h.open();
|
|
@@ -368,7 +370,7 @@ test('Change, a toggle, then Use this: the toggle stands as the answer', async (
|
|
|
368
370
|
assert.equal(h.confirmed(), true, 'and the row is confirmed');
|
|
369
371
|
});
|
|
370
372
|
|
|
371
|
-
test('Looks right CONFIRMS the set on screen — looking at it and accepting it is not the same as never being asked', async () => {
|
|
373
|
+
test('Looks right, continue CONFIRMS the set on screen — looking at it and accepting it is not the same as never being asked', async () => {
|
|
372
374
|
const h = boot({ state: { projType: 'game' } });
|
|
373
375
|
await h.open();
|
|
374
376
|
|
|
@@ -243,7 +243,7 @@ test('a resumed draft past the code row still probes repo access before Create',
|
|
|
243
243
|
'every screen between the review and the done panel re-probes');
|
|
244
244
|
});
|
|
245
245
|
|
|
246
|
-
test('a half-typed custom domain says why
|
|
246
|
+
test('a half-typed custom domain says why Done refuses it; a taken handle silences the format cheer', () => {
|
|
247
247
|
assert.match(SCRIPT, /That doesn’t look like a domain yet \(e\.g\. moonbase\.example\.com\) — fix it, or clear the field to use the address above\./);
|
|
248
248
|
assert.match(SCRIPT, /if \(avail === 'taken' \|\| avail === 'yours' \|\| avail === 'reserved'\) \{ var hs = \$\('h-slug'\); hs\.innerHTML = ' '; hs\.className = 'hint'; \}/);
|
|
249
249
|
});
|