@bongos/core 1.21.30 → 1.21.32
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 +47 -37
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +7 -0
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/api/openapi.json +125 -3
- package/docs/api-reference.md +4 -2
- package/docs/copy-inventory.md +393 -408
- package/docs/copy-registry.json +498 -663
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-readings.json +655 -665
- package/modules/government/catalog.js +1 -0
- package/modules/government/migrations/government_024_version_focus_set.sql +19 -0
- package/modules/hall-ui/public/roadmap.css +5 -0
- package/modules/hall-ui/public/roadmap.js +64 -5
- package/modules/lifecycle/routes/versions.js +20 -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/bongos/project-settings.js +28 -0
- package/src/module-api.js +3 -1
- package/tests/focus_version.mjs +96 -0
- 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
|
@@ -164,17 +164,17 @@
|
|
|
164
164
|
{ "name": "the quick checks (task 1004504): a country with states asks for one, a yes to under-18s shows the age tiers, an existing entity opens the full list, and Continue reaches the review", "auth": true, "width": 1440, "url": "/projects?view=new",
|
|
165
165
|
"steps": [["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["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],["click","#physHaveEntity"],["wait",100],["click","[data-phys=\"entity\"][data-val=\"for-profit\"]"],["click","#physNext"],["wait",700]],
|
|
166
166
|
"expect": {"visible":["#panel-review","#reviewRows .setRow[data-row=\"code\"]"],"hidden":["#panel-checks"]} },
|
|
167
|
-
{ "name": "the review
|
|
168
|
-
"steps": [["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["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",200],["click","[data-phys-solo=\"many\"]"],["wait",600],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-
|
|
169
|
-
"expect": {"visible":["#panel-review","#reviewRows .setRow[data-row=\"rewards\"]","#reviewRows .setRow.done[data-row=\"team\"]"]
|
|
167
|
+
{ "name": "the review (tasks 1004504, 1004550): Edit opens a row in place, Done confirms it, and Continue is the one confirmation for the rest", "auth": true, "width": 1440, "url": "/projects?view=new",
|
|
168
|
+
"steps": [["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["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",200],["click","[data-phys-solo=\"many\"]"],["wait",600],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-chg]"],["wait",200]],
|
|
169
|
+
"expect": {"visible":["#panel-review","#reviewRows .setRow[data-row=\"rewards\"]","#reviewRows .setRow.done[data-row=\"team\"]"]} },
|
|
170
170
|
{ "name": "the wizard driver (tasks 1003346 and 1004504): the fork, what, the checks, every review row confirmed (the stub grants repo access, so the new repository is named from the project; the dotless stub host takes a domain the founder owns), the look, then the filing — the done panel with the standup strip and the human steps", "auth": true, "width": 1440, "url": "/projects?view=new",
|
|
171
|
-
"steps": [["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["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=\"
|
|
171
|
+
"steps": [["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["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]],
|
|
172
172
|
"expect": {"visible":["#panel-done","#liveProgress .prog","#liveProgress .prog .stages li.now","#humanSteps li"],"hidden":["#panel-look","#panel-review"],"text":[["#done-h","Setting up Mercury"]]} },
|
|
173
173
|
{ "name": "the front door at 390: the fork's two doors stack inside one pill", "auth": true, "width": 390, "url": "/projects?view=new",
|
|
174
174
|
"steps": [["wait",600],["click","#termsAck"],["wait",100]],
|
|
175
175
|
"expect": {"visible":["#panel-fork",".doorPill","#wizDemo","#wizStart"]} },
|
|
176
176
|
{ "name": "the wizard driver at 390: the review rows stack and the walk still reaches Create", "auth": true, "width": 390, "url": "/projects?view=new",
|
|
177
|
-
"steps": [["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["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=\"
|
|
177
|
+
"steps": [["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["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]],
|
|
178
178
|
"expect": {"visible":["#panel-look","#wizLook","#doCreate"],"hidden":["#panel-review"],"text":[["#look-h","Give it a look."]]} }
|
|
179
179
|
]
|
|
180
180
|
}
|
|
@@ -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.32",
|
|
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.32",
|
|
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.32",
|
|
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
|
@@ -8936,5 +8936,17 @@
|
|
|
8936
8936
|
"id": "1004493",
|
|
8937
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
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
|
+
}
|
|
8945
|
+
],
|
|
8946
|
+
"1.21.32": [
|
|
8947
|
+
{
|
|
8948
|
+
"id": "1004551",
|
|
8949
|
+
"text": "An Archon can star a version on the Roadmap as the project's focus; it persists across reloads, and everyone else sees the star read-only (the server refuses non-Archons)."
|
|
8950
|
+
}
|
|
8939
8951
|
]
|
|
8940
8952
|
}
|
|
@@ -48,6 +48,7 @@ const { logger } = require('./logger');
|
|
|
48
48
|
// cannot collide with this one.
|
|
49
49
|
const SETTING_KEYS = {
|
|
50
50
|
ROT_DAYS: 'attention.rot_days',
|
|
51
|
+
FOCUS_VERSION: 'roadmap.focus_version',
|
|
51
52
|
};
|
|
52
53
|
|
|
53
54
|
// The last-resort rot timer, in days. 30 because that is the shortest window in
|
|
@@ -176,6 +177,31 @@ function describeRotTimer(resolved) {
|
|
|
176
177
|
return `${base} — ignoring ${r.invalid.where}="${r.invalid.raw}" (${r.invalid.reason})`;
|
|
177
178
|
}
|
|
178
179
|
|
|
180
|
+
// getFocusVersionId() — the version the project is focused on, or null.
|
|
181
|
+
// NO ROW (or an empty value) MEANS NO FOCUS: the focus is chosen by an Archon in the
|
|
182
|
+
// UI (task 1004551), never inferred in code, so absence is an answer and a default is
|
|
183
|
+
// never seeded. Whether the id still names a real version is the caller's concern —
|
|
184
|
+
// the write path refuses unknown ids, and a reader of a since-deleted version simply
|
|
185
|
+
// finds no card to star.
|
|
186
|
+
async function getFocusVersionId() {
|
|
187
|
+
const row = await getSetting(SETTING_KEYS.FOCUS_VERSION);
|
|
188
|
+
return row && row.value ? String(row.value) : null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// setFocusVersionId(versionId, builderId) — set the focus, or clear it with null.
|
|
192
|
+
// Clearing deletes the row rather than storing '' so "never set" and "cleared" are the
|
|
193
|
+
// same state, as the null-is-meaningful rule on getSetting requires.
|
|
194
|
+
async function setFocusVersionId(versionId, builderId = null) {
|
|
195
|
+
if (versionId == null || versionId === '') {
|
|
196
|
+
await pool.query('DELETE FROM project_settings WHERE key = $1', [SETTING_KEYS.FOCUS_VERSION]);
|
|
197
|
+
// The row is gone, so updated_by cannot say who cleared it; the log line does.
|
|
198
|
+
logger.info(`project focus version cleared by builder ${builderId == null ? 'unknown' : builderId}`);
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
await setSetting(SETTING_KEYS.FOCUS_VERSION, versionId, builderId);
|
|
202
|
+
return String(versionId);
|
|
203
|
+
}
|
|
204
|
+
|
|
179
205
|
// parseDays is exported for the tests only — the range check and the default are
|
|
180
206
|
// asserted against the SAME constants the resolver reads, because a test that
|
|
181
207
|
// re-typed 30 or 365 would be the second copy of the number this file exists to
|
|
@@ -190,4 +216,6 @@ module.exports = {
|
|
|
190
216
|
setSetting,
|
|
191
217
|
resolveRotDays,
|
|
192
218
|
describeRotTimer,
|
|
219
|
+
getFocusVersionId,
|
|
220
|
+
setFocusVersionId,
|
|
193
221
|
};
|
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.32'; // 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');
|
|
@@ -444,6 +444,8 @@ module.exports = {
|
|
|
444
444
|
get KEYS() { return require('./bongos/project-settings').SETTING_KEYS; },
|
|
445
445
|
get get() { return require('./bongos/project-settings').getSetting; },
|
|
446
446
|
get set() { return require('./bongos/project-settings').setSetting; },
|
|
447
|
+
get getFocusVersionId() { return require('./bongos/project-settings').getFocusVersionId; },
|
|
448
|
+
get setFocusVersionId() { return require('./bongos/project-settings').setFocusVersionId; },
|
|
447
449
|
get describeRotTimer() { return require('./bongos/project-settings').describeRotTimer; },
|
|
448
450
|
// The RANGE CHECK, shared rather than re-typed. The settings page validates a
|
|
449
451
|
// submitted timer with the very parser the resolver reads it back through, so the
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// tests/focus_version.mjs — the roadmap focus star (task 1004551): the settings accessor,
|
|
2
|
+
// the Archon-only write route and the any-builder read, run against a fake pool and fake
|
|
3
|
+
// requests. No DB needed.
|
|
4
|
+
//
|
|
5
|
+
// Run: node --test tests/focus_version.mjs
|
|
6
|
+
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
import { test } from 'node:test';
|
|
9
|
+
import { readFileSync } from 'node:fs';
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
|
+
|
|
12
|
+
process.env.NODE_ENV = 'test';
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
|
|
15
|
+
// A one-table fake pool, installed before project-settings loads.
|
|
16
|
+
const store = new Map();
|
|
17
|
+
const poolPath = require.resolve('../src/bongos/pool.js');
|
|
18
|
+
require(poolPath);
|
|
19
|
+
require.cache[poolPath].exports.pool = {
|
|
20
|
+
async query(sql, p) {
|
|
21
|
+
if (/^\s*SELECT/i.test(sql)) { const v = store.get(p[0]); return { rows: v == null ? [] : [{ key: p[0], value: v }] }; }
|
|
22
|
+
if (/^\s*INSERT/i.test(sql)) { store.set(p[0], p[1]); return { rows: [{ key: p[0], value: p[1] }] }; }
|
|
23
|
+
if (/^\s*DELETE/i.test(sql)) { store.delete(p[0]); return { rows: [] }; }
|
|
24
|
+
throw new Error(`unexpected sql: ${sql}`);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const ps = require('../src/bongos/project-settings.js');
|
|
28
|
+
const db = require('../modules/lifecycle/db.js');
|
|
29
|
+
const router = require('../modules/lifecycle/routes/versions.js')();
|
|
30
|
+
|
|
31
|
+
const src = (rel) => readFileSync(new URL('../' + rel, import.meta.url), 'utf8');
|
|
32
|
+
db.getVersion = async (id) => (id === 'BONGOS-V2' ? { id } : null);
|
|
33
|
+
|
|
34
|
+
// The route's own handler is the LAST layer on its stack (after the auth middleware).
|
|
35
|
+
function handler(method, path) {
|
|
36
|
+
const layer = router.stack.find((l) => l.route && l.route.path === path && l.route.methods[method]);
|
|
37
|
+
assert.ok(layer, `${method} ${path} is registered`);
|
|
38
|
+
return { chain: layer.route.stack, last: layer.route.stack[layer.route.stack.length - 1].handle };
|
|
39
|
+
}
|
|
40
|
+
async function call(method, path, req) {
|
|
41
|
+
const out = { status: 200, body: null };
|
|
42
|
+
const res = {
|
|
43
|
+
status(n) { out.status = n; return this; },
|
|
44
|
+
json(b) { out.body = b; return this; },
|
|
45
|
+
fail(code, o) { const a = typeof o === 'number' ? { status: o } : (o || {}); out.status = a.status; out.body = { error: { code, message: a.message || code } }; return this; },
|
|
46
|
+
};
|
|
47
|
+
await handler(method, path).last({ body: {}, builder: { id: 9, rank: 'archon' }, ...req }, res, (e) => { throw e; });
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
test('no row means no focus', async () => {
|
|
52
|
+
store.clear();
|
|
53
|
+
assert.equal(await ps.getFocusVersionId(), null);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('PUT stores a known version and GET returns it', async () => {
|
|
57
|
+
store.clear();
|
|
58
|
+
const put = await call('put', '/versions/focus', { body: { version_id: 'BONGOS-V2' } });
|
|
59
|
+
assert.equal(put.body.focus_version_id, 'BONGOS-V2');
|
|
60
|
+
assert.equal(store.get(ps.SETTING_KEYS.FOCUS_VERSION), 'BONGOS-V2');
|
|
61
|
+
const got = await call('get', '/versions/focus', {});
|
|
62
|
+
assert.deepEqual(got.body, { focus_version_id: 'BONGOS-V2', can_set: true });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('PUT with null clears the focus (the row is gone, not blank)', async () => {
|
|
66
|
+
store.set(ps.SETTING_KEYS.FOCUS_VERSION, 'BONGOS-V2');
|
|
67
|
+
const put = await call('put', '/versions/focus', { body: { version_id: null } });
|
|
68
|
+
assert.equal(put.body.focus_version_id, null);
|
|
69
|
+
assert.equal(store.has(ps.SETTING_KEYS.FOCUS_VERSION), false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('an unknown version is refused with a readable message and nothing is stored', async () => {
|
|
73
|
+
store.clear();
|
|
74
|
+
const put = await call('put', '/versions/focus', { body: { version_id: 'NOPE' } });
|
|
75
|
+
assert.equal(put.status, 404);
|
|
76
|
+
assert.match(put.body.error.message, /NOPE/);
|
|
77
|
+
assert.equal(store.size, 0);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('a missing or blank version_id is refused, not treated as a clear', async () => {
|
|
81
|
+
for (const body of [{}, { version_id: ' ' }]) {
|
|
82
|
+
const put = await call('put', '/versions/focus', { body });
|
|
83
|
+
assert.equal(put.status, 400);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('a non-Archon reads the focus but is told it cannot set it', async () => {
|
|
88
|
+
store.set(ps.SETTING_KEYS.FOCUS_VERSION, 'BONGOS-V2');
|
|
89
|
+
const got = await call('get', '/versions/focus', { builder: { id: 4, rank: 'metic' } });
|
|
90
|
+
assert.deepEqual(got.body, { focus_version_id: 'BONGOS-V2', can_set: false });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('the write route carries the Archon-only permission gate; the read route does not', () => {
|
|
94
|
+
assert.match(src('modules/lifecycle/routes/versions.js'), /router\.put\('\/versions\/focus',\s*auth\.requireBuilder,\s*auth\.requirePermission\('version\.focus\.set'\)/);
|
|
95
|
+
assert.doesNotMatch(src('modules/lifecycle/routes/versions.js').match(/router\.get\('\/versions\/focus',[^\n]*/)[0], /requireRank/);
|
|
96
|
+
});
|
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
|
});
|