@bongos/core 1.21.4 → 1.21.6

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.
Files changed (126) hide show
  1. package/.bongos-core.json +233 -108
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +2 -0
  4. package/clients/bongos-client/index.cjs +2 -0
  5. package/clients/bongos-client/index.d.ts +3 -1
  6. package/clients/bongos-client/index.mjs +2 -0
  7. package/docs/adr/0361-merges-publish-as-candidates-release-decides-what-others-are-offered.md +1 -0
  8. package/docs/adr/0362-the-governor-docket-is-collected-from-contributed-sources.md +34 -0
  9. package/docs/adr/README.md +1 -0
  10. package/docs/api/openapi.json +37 -2
  11. package/docs/api-reference.md +4 -3
  12. package/docs/copy-inventory.md +541 -658
  13. package/docs/copy-registry.json +1205 -2340
  14. package/docs/module-api-changelog.md +4 -0
  15. package/docs/onboarding/diagrams/03-drachmae-karma.mmd +1 -1
  16. package/docs/onboarding/diagrams/assertions.json +1 -1
  17. package/docs/page-inventory.json +47 -28
  18. package/docs/page-readings.json +1128 -1339
  19. package/modules/autonomy/config-idle.js +192 -0
  20. package/modules/autonomy/governor-docket.js +101 -0
  21. package/modules/autonomy/routes/autonomy.js +27 -0
  22. package/modules/autonomy/runner-health.js +22 -1
  23. package/modules/discord/board-broadcast.js +3 -1
  24. package/modules/discord/craft-broadcast.js +107 -0
  25. package/modules/discord/routes/discord.js +10 -0
  26. package/modules/discord/ship-broadcast.js +30 -2
  27. package/modules/economy/reward.js +3 -0
  28. package/modules/government/board.js +20 -5
  29. package/modules/government/docket-vocab.js +82 -0
  30. package/modules/government/docket.js +458 -0
  31. package/modules/government/routes/government.js +34 -0
  32. package/modules/hall-ui/public/city-draw.js +438 -0
  33. package/modules/hall-ui/public/city.css +110 -0
  34. package/modules/hall-ui/public/city.html +71 -0
  35. package/modules/hall-ui/public/city.js +177 -0
  36. package/modules/hall-ui/public/city.states.json +30 -0
  37. package/modules/hall-ui/public/gate.js +11 -4
  38. package/modules/hall-ui/public/genesis-home.js +9 -10
  39. package/modules/hall-ui/public/profile.css +4 -0
  40. package/modules/hall-ui/public/profile.js +25 -1
  41. package/modules/hall-ui/public/settings-autobongos.js +19 -1
  42. package/modules/hall-ui/public/settings.css +26 -0
  43. package/modules/hall-ui/public/settings.html +8 -0
  44. package/modules/hall-ui/public/settings.js +43 -3
  45. package/modules/ideas/governor-docket.js +93 -0
  46. package/modules/ideas/routes/blockers.js +5 -0
  47. package/modules/lifecycle/governor-docket.js +192 -0
  48. package/modules/lifecycle/routes/gate-approvals.js +78 -33
  49. package/modules/lifecycle/routes/lifecycle.js +3 -0
  50. package/modules/onboarding/routes/access-requests.js +68 -27
  51. package/modules/platform-identity/craft-rollup.js +72 -0
  52. package/modules/platform-identity/migrations/platform_identity_029_activity_crafts.sql +23 -0
  53. package/modules/platform-identity/platform-identity.js +12 -6
  54. package/modules/platform-identity/routes/sso.js +4 -0
  55. package/modules/platform-identity/tests/platform-identity.mjs +1 -1
  56. package/modules/provisioning/core-upgrade.js +34 -7
  57. package/modules/provisioning/module.json +2 -1
  58. package/modules/provisioning/seams.js +2 -0
  59. package/modules/public-landing/public/assets/cosmos.css +6 -50
  60. package/modules/public-landing/public/projects.html +1170 -1659
  61. package/modules/public-landing/public/projects.probes.json +18 -18
  62. package/modules/public-landing/public/projects.states.json +24 -31
  63. package/modules/security/routes/reports.js +39 -0
  64. package/package-lock.json +2 -2
  65. package/package.json +1 -1
  66. package/release-notes.json +72 -0
  67. package/scripts/gds/autobongos-grade-cap.js +187 -0
  68. package/scripts/gds/autobongos-loop.js +4 -0
  69. package/scripts/gds/autobongos-run.js +154 -13
  70. package/scripts/gds/autobongos-verify.js +211 -14
  71. package/scripts/gds/provision-core-upgrade.js +14 -2
  72. package/scripts/gds/provision-repo.js +77 -13
  73. package/scripts/gds/ship-flow.js +5 -0
  74. package/scripts/gds/ship-preflight-steps.js +2 -1
  75. package/scripts/gds/ship.js +10 -0
  76. package/scripts/gds/update-sweep.js +24 -8
  77. package/scripts/gds/upgrade-outcome.js +1 -0
  78. package/src/bongos/auth-admission.js +95 -13
  79. package/src/bongos/db-kernel.js +1 -1
  80. package/src/bongos/module-rechecks.js +77 -0
  81. package/src/bongos/route-rank-check.js +3 -0
  82. package/src/bongos/routes/core-update.js +68 -2
  83. package/src/bongos/routes/modules.js +17 -0
  84. package/src/bongos/serve-internal.js +14 -0
  85. package/src/bongos/software-update.js +3 -2
  86. package/src/module-api.js +1 -1
  87. package/tests/activity_rollup_order_db.mjs +3 -1
  88. package/tests/activity_snapshots_db.mjs +2 -0
  89. package/tests/autobongos_grade_cap.mjs +125 -0
  90. package/tests/autobongos_loop.mjs +230 -1
  91. package/tests/autobongos_verify.mjs +239 -3
  92. package/tests/autonomy_config_idle.mjs +272 -0
  93. package/tests/core_update_banner.mjs +29 -0
  94. package/tests/core_upgrade_door.mjs +31 -1
  95. package/tests/core_upgrade_runner.mjs +20 -0
  96. package/tests/discord_craft_broadcast.mjs +162 -0
  97. package/tests/genesis_home.mjs +1 -1
  98. package/tests/government_routes.mjs +3 -1
  99. package/tests/governor_docket.mjs +552 -0
  100. package/tests/hall_audit.mjs +10 -1
  101. package/tests/hall_city.mjs +369 -0
  102. package/tests/hall_page_gate_map.mjs +5 -0
  103. package/tests/hub_craft_rollup.mjs +350 -0
  104. package/tests/onboard.mjs +10 -4
  105. package/tests/platform_boot.mjs +1 -1
  106. package/tests/profile_rollup_consent.mjs +2 -0
  107. package/tests/profile_route.mjs +16 -2
  108. package/tests/projects_hub.mjs +196 -89
  109. package/tests/projects_hub_app_status.mjs +3 -1
  110. package/tests/projects_hub_app_step.mjs +72 -48
  111. package/tests/projects_hub_dns_ready.mjs +10 -2
  112. package/tests/projects_hub_look.mjs +32 -22
  113. package/tests/projects_hub_module_picker.mjs +112 -62
  114. package/tests/projects_hub_pre_uat.mjs +48 -41
  115. package/tests/provision.mjs +78 -0
  116. package/tests/public_landing_projects.mjs +12 -6
  117. package/tests/settings_main_role.mjs +190 -0
  118. package/tests/software_update.mjs +56 -1
  119. package/tests/update_subscription_engine.mjs +55 -3
  120. package/tests/wizard_demo.mjs +37 -11
  121. package/tests/wizard_draft_resume.mjs +34 -22
  122. package/tests/wizard_front_door.mjs +173 -103
  123. package/tests/wizard_intent_resume.mjs +28 -25
  124. package/tests/wizard_physics.mjs +94 -76
  125. package/tests/wizard_physics_more.mjs +428 -277
  126. package/tests/wizard_six_screens.mjs +126 -0
@@ -1,11 +1,12 @@
1
1
  // tests/wizard_physics.mjs — planet physics, questions 1 to 4, in the create wizard
2
- // (task 1004418 / BV2.PS07, goal 1000121).
2
+ // (task 1004418 / BV2.PS07, goal 1000121; regrouped by task 1004504).
3
3
  //
4
- // Done-when: the four questions and their branches work end to end (state incl. the
5
- // existing-project branch and the stop screen; geography; ages with the guardian
6
- // tiers; entity, intent when new and legal type when it exists), the planet states
7
- // match the canvas, answers save through the create's `detail`, and the options the
8
- // soft-limit table hides or greys show their reason (spec D6).
4
+ // Done-when: the questions and their branches work end to end (geography; ages with
5
+ // the guardian tiers; entity, intent when new and legal type when it exists; the stop
6
+ // screen), answers save through the create's `detail`, and the options the soft-limit
7
+ // table hides or greys show their reason (spec D6). Since task 1004504 geography, ages
8
+ // and entity are the "few quick checks" screen and the project's state is the review's
9
+ // Code row; the forming planet left with the rail (the night room replaced it).
9
10
  //
10
11
  // Three layers, all RUN: the server's pre-create check over a mounted router; the
11
12
  // page's vocabularies against planet-physics.js (a page cannot require a module, so
@@ -93,28 +94,41 @@ test('a builder who loops the check is slowed down, and another builder is not',
93
94
  });
94
95
 
95
96
  // ── the page's vocabularies are the server's ─────────────────────────────────
96
- // questions 5 to 7 joined the same region (task 1004419, tests/wizard_physics_more.mjs)
97
- const SCRIPT = HUB.slice(HUB.indexOf('/* ── planet physics, questions 1 to 7 (tasks 1004418 and 1004419, BV2.PS07 + PS11) ──'),
98
- HUB.indexOf(' /* per-panel validation on Next */'));
97
+ // questions 5 to 7 joined the same region (task 1004419, tests/wizard_physics_more.mjs);
98
+ // the region runs to the row checks the old steps made on Next (task 1004504)
99
+ const START = '/* ── planet physics (tasks 1004418 and 1004419, BV2.PS07 + PS11; regrouped by task 1004504) ──';
100
+ const END = ' /* ── checks the old steps made on Next, now made by their rows ── */';
101
+ assert.ok(HUB.indexOf(START) > -1, 'the physics region opens with its sentinel comment');
102
+ assert.ok(HUB.indexOf(END, HUB.indexOf(START)) > HUB.indexOf(START), 'and closes where the row checks begin');
103
+ const SCRIPT = HUB.slice(HUB.indexOf(START), HUB.indexOf(END, HUB.indexOf(START)));
99
104
 
100
105
  test('the page carries the server\'s countries, regions and entity forms, exactly', () => {
101
106
  assert.ok(SCRIPT.length > 1000, 'the physics controller was found');
107
+ assert.ok(SCRIPT.includes('function checksAdvance()'), 'and it carries the quick checks\' Continue');
102
108
  const box = {};
103
109
  vm.createContext(box);
104
- vm.runInContext(SCRIPT.slice(0, SCRIPT.indexOf(' var PHYS_LAST')).replace(/\bvar /g, 'this.'), box);
110
+ vm.runInContext(SCRIPT.slice(0, SCRIPT.indexOf(' var physLimits = [];')).replace(/\bvar /g, 'this.'), box);
105
111
  const plain = (v) => JSON.parse(JSON.stringify(v)); // values born in the vm
106
112
  assert.deepEqual(plain(box.PHYS_COUNTRIES), [...physics.COUNTRY_CODES]);
107
113
  assert.deepEqual(Object.keys(box.PHYS_REGIONS).sort(), [...physics.REGION_CODES].sort());
108
114
  assert.deepEqual(plain(box.PHYS_ENTITY_FORMS), [...physics.ENTITY_FORMS]);
109
- for (const k of Object.keys(box.PHYS_MATERIAL)) {
115
+ // every entity the page has words for is an entity answer (PHYS_MATERIAL, the plates
116
+ // per entity, left with the forming planet in task 1004504)
117
+ assert.equal(box.PHYS_MATERIAL, undefined, 'the per-entity plates are gone');
118
+ for (const k of Object.keys(box.PHYS_SAY.entity)) {
110
119
  assert.notEqual(physics.canonicalAnswer(physics.PLANET_PHYSICS_QUESTIONS.entity, k), undefined, `${k} is an entity answer`);
111
120
  }
112
121
  });
113
122
 
114
123
  test('every choice the questions offer is an answer the server accepts', () => {
115
- const panel = HUB.slice(HUB.indexOf('id="panel-physics"'), HUB.indexOf('<!-- step 4 · name'));
124
+ // the quick checks, and the rows of the review that host the rest (task 1004504)
125
+ const panel = HUB.slice(HUB.indexOf('<section id="view-new"'), HUB.indexOf('<!-- 5 · a look, then Create'));
126
+ const checks = HUB.slice(HUB.indexOf('id="panel-checks"'), HUB.indexOf('<!-- 4 · check what we set up'));
127
+ assert.deepEqual([...new Set([...checks.matchAll(/data-phys="([a-z_]+)"/g)].map((m) => m[1]))], ['builder_geography', 'minors_on_board', 'entity'],
128
+ 'the quick checks ask where builders will be, about under-18s and who is behind it');
129
+ assert.match(checks, /<select id="physCountry">/, 'and where you are');
116
130
  const seen = [...panel.matchAll(/data-phys="([a-z_]+)" data-val="([^"]+)"/g)];
117
- assert.ok(seen.length >= 20, 'the four questions\' choices');
131
+ assert.ok(seen.length >= 20, 'the questions\' choices');
118
132
  for (const [, key, raw] of seen) {
119
133
  const one = raw === 'true' ? true : raw === 'false' ? false : raw;
120
134
  // a list question's choice is one member of the list it sends (task 1004419)
@@ -128,9 +142,10 @@ test('every choice the questions offer is an answer the server accepts', () => {
128
142
  });
129
143
 
130
144
  test('the age tiers are described, never claimed as enforced (spec D6)', () => {
131
- const ages = HUB.slice(HUB.indexOf('id="physAges"'), HUB.indexOf('<div class="physQ" data-q="4"'));
145
+ const ages = HUB.slice(HUB.indexOf('id="physAges"'), HUB.indexOf('id="physIntent"'));
146
+ assert.ok(ages.length > 100, 'the age tiers sit between the under-18 question and who is behind it');
132
147
  assert.match(ages, /13 to 15<\/dt><dd>Non-monetary tracks only/);
133
- assert.match(ages, /the platform does not check ages for you yet/);
148
+ assert.match(ages, /[Tt]he platform does not check ages for you yet/);
134
149
  assert.doesNotMatch(ages, /apply automatically|enforced/i);
135
150
  });
136
151
 
@@ -157,11 +172,13 @@ function boot({ checkAnswer = { stop: false, soft_limits: { limits: [] } } } = {
157
172
  const $ = (id) => (els[id] = els[id] || new El(id));
158
173
  const shown = [];
159
174
  const fetched = [];
160
- const state = { description: '', teamShape: null, physics: null };
175
+ const state = { description: '', teamShape: null, physics: null, mode: 'greenfield', confirmed: {} };
161
176
  const sandbox = {
162
177
  $, state, API: '/api/bongos', document: { createElement: () => new El('opt') },
163
178
  root: { querySelectorAll: () => [], querySelector: () => null },
164
179
  saveDraft() {}, show(n) { shown.push(n); }, Intl,
180
+ // the review's machinery lives outside this region (tests/wizard_physics_more.mjs runs it)
181
+ paintSetup() {}, unconfirm() {}, setMode(m) { state.mode = m; }, authKnown: true,
165
182
  fetch: async (url, opts) => { fetched.push({ url, body: opts && opts.body && JSON.parse(opts.body) }); return { ok: true, json: async () => (url.includes('physics-check') ? checkAnswer : []) }; },
166
183
  };
167
184
  vm.createContext(sandbox);
@@ -170,51 +187,56 @@ function boot({ checkAnswer = { stop: false, soft_limits: { limits: [] } } } = {
170
187
  }
171
188
  const tick = () => new Promise((r) => setTimeout(r, 0));
172
189
 
173
- test('Continue walks the questions and moves the wizard on only after the last', async () => {
174
- const w = boot();
190
+ test('Continue on the quick checks asks for each answer, then the server, and only then moves on', async () => {
191
+ const w = boot({ checkAnswer: { stop: false, soft_limits: { limits: [{ option: 'equity', effect: 'note', reason: 'r' }] } } });
175
192
  const a = () => w.state.physics.answers;
176
- assert.equal(w.run('physAdvance()'), false, 'the step never says yes itself');
177
- assert.equal(w.state.physics.q, 1, 'from the start screen to question 1');
178
- w.run('physAdvance()');
179
- assert.equal(w.state.physics.q, 1, 'question 1 unanswered: it stays');
180
- assert.match(w.$('h-phys').textContent, /new or existing/);
181
- a().project_state = 'existing';
182
- w.run('physAdvance()');
183
- assert.match(w.$('h-phys').textContent, /what already exists/i, 'an existing planet must say what exists');
184
- a().existing_work = ['code', 'digital-files']; a().main_work = 'code';
185
- w.state.description = 'A co-op farming game.';
186
- w.run('physAdvance()');
187
- await tick(); await tick();
188
- assert.equal(w.state.physics.q, 2, 'the screen passed it, on to geography');
189
- assert.deepEqual(w.fetched.at(-1).body, { description: 'A co-op farming game.', detail: { project_state: 'existing', existing_work: ['code', 'digital-files'], main_work: 'code' } });
190
- w.run('physAdvance()');
191
- assert.equal(w.state.physics.q, 2, 'no country: it stays');
192
- a().home_country = 'US'; a().home_region = 'US-CA'; a().builder_geography = 'us-eu';
193
- w.run('physAdvance()');
194
- assert.equal(w.state.physics.q, 3);
193
+ assert.equal(w.run('checksAdvance()'), false, 'the screen never says yes itself');
194
+ assert.match(w.$('h-phys').textContent, /Pick your country/);
195
+ a().home_country = 'US'; a().home_region = 'US-CA';
196
+ w.run('checksAdvance()');
197
+ assert.match(w.$('h-phys').textContent, /where builders will be/);
198
+ a().builder_geography = 'us-eu';
199
+ w.run('checksAdvance()');
200
+ assert.match(w.$('h-phys').textContent, /under 18/);
195
201
  a().minors_on_board = true;
196
- w.run('physAdvance()');
197
- assert.equal(w.state.physics.q, 4);
198
- w.run('physAdvance()');
199
- assert.deepEqual(w.shown, [], 'no entity: still here');
202
+ w.run('checksAdvance()');
203
+ assert.match(w.$('h-phys').textContent, /who’s behind it/);
204
+ assert.equal(w.fetched.length, 0, 'nothing is asked of the server until every answer is given');
200
205
  a().entity = 'for-profit';
201
- w.run('physAdvance()');
206
+ w.state.description = 'A co-op farming game.';
207
+ w.run('checksAdvance()');
208
+ w.run('checksAdvance()');
202
209
  await tick(); await tick();
203
- // task 1004419: questions 5 to 7 and the summary follow inside the same step
204
- // (walked to the name step in tests/wizard_physics_more.mjs)
205
- assert.equal(w.state.physics.q, 5, 'on to question 5');
206
- assert.deepEqual(w.shown, [], 'and the wizard has not moved on');
210
+ assert.equal(w.fetched.length, 1, 'a second Continue while the first is out asks nothing more');
211
+ assert.equal(w.fetched[0].url, '/api/bongos/provisioning/physics-check');
212
+ // the review's prefills are written on the way: a new project, one person, all managed
213
+ assert.deepEqual(w.fetched[0].body, { description: 'A co-op farming game.', detail: {
214
+ project_state: 'new', home_country: 'US', home_region: 'US-CA', builder_geography: 'us-eu', minors_on_board: true, entity: 'for-profit',
215
+ builders_at_start: 1, builders_expected: 1, framing: 'managed', voice: 'professional' } });
216
+ assert.equal(w.state.teamShape, 'solo', 'one person is a solo team shape');
217
+ assert.equal(w.$('h-phys').textContent, ' ', 'the ask clears');
218
+ assert.deepEqual(w.shown, [4], 'the screen passed it: on to the review');
219
+ assert.equal(JSON.stringify(w.run('physLimits')), JSON.stringify([{ option: 'equity', effect: 'note', reason: 'r' }]), 'and its soft limits shape the review');
207
220
  });
208
221
 
209
- test('a stopped description lands on the stop screen with the server\'s words', async () => {
222
+ test('a stopped description lands on the stop screen with the server\'s words, and Start over clears it all', async () => {
210
223
  const w = boot({ checkAnswer: { stop: true, message: STOP_MESSAGE, soft_limits: { limits: [] } } });
211
- w.run('physAdvance()');
212
- w.state.physics.answers.project_state = 'new';
224
+ w.run('phys()');
225
+ Object.assign(w.state.physics.answers, { home_country: 'NL', builder_geography: 'global', minors_on_board: false, entity: 'intent-solo' });
213
226
  w.state.description = 'ransomware';
214
- w.run('physAdvance()');
227
+ w.run('checksAdvance()');
215
228
  await tick(); await tick();
216
- assert.equal(w.state.physics.q, 'stop');
217
229
  assert.equal(w.$('physStopMsg').textContent, STOP_MESSAGE);
230
+ assert.equal(w.$('checksStop').hidden, false, 'the stop screen shows');
231
+ assert.equal(w.$('checksMain').hidden, true, 'in place of the questions');
232
+ assert.deepEqual(w.shown, [], 'and nothing moves on: nothing exists to undo');
233
+ w.state.confirmed = { code: true };
234
+ w.$('physStartOver').listeners.click();
235
+ assert.equal(w.state.description, '');
236
+ assert.equal(JSON.stringify(w.state.physics.answers), '{}', 'every answer goes');
237
+ assert.equal(JSON.stringify(w.state.confirmed), '{}', 'and every confirmed row');
238
+ assert.deepEqual(w.shown, [2], 'back to "What are you making?"');
239
+ assert.equal(w.$('checksMain').hidden, false, 'with the questions back in place');
218
240
  });
219
241
 
220
242
  test('the create sends the answers as detail: existing work only for an existing planet, the team shape beside them', () => {
@@ -223,49 +245,45 @@ test('the create sends the answers as detail: existing work only for an existing
223
245
  Object.assign(w.state.physics.answers, { project_state: 'new', existing_work: ['code'], main_work: 'code', home_country: 'NL', builder_geography: 'global', minors_on_board: false, entity: 'intent-solo' });
224
246
  w.state.teamShape = 'solo';
225
247
  assert.deepEqual(JSON.parse(JSON.stringify(w.run('createDetail()'))), {
226
- project_state: 'new', home_country: 'NL', builder_geography: 'global', minors_on_board: false, entity: 'intent-solo', voice: 'planet', team_shape: 'solo',
227
- });
248
+ project_state: 'new', home_country: 'NL', builder_geography: 'global', minors_on_board: false, entity: 'intent-solo', voice: 'professional', team_shape: 'solo',
249
+ }, 'one plain voice since task 1004504: an individual is sent the professional voice too');
250
+ w.state.physics.answers.entity = 'for-profit';
251
+ assert.equal(w.run('createDetail()').voice, 'professional', 'and so is an organization');
252
+ w.state.physics.answers.project_state = 'existing';
253
+ assert.deepEqual(JSON.parse(JSON.stringify(w.run('createDetail()'))).existing_work, ['code'], 'an existing planet sends what exists');
254
+ w.state.demo = { people: 3, hours_each: 4 };
255
+ assert.deepEqual(JSON.parse(JSON.stringify(w.run('createDetail()'))), { team_shape: 'solo' }, 'a demo sends only its team shape');
256
+ w.state.demo = null;
228
257
  w.state.physics.answers = {}; w.state.teamShape = null;
229
258
  assert.equal(w.run('createDetail()'), undefined, 'nothing answered: the detail is omitted, as before');
230
259
  });
231
260
 
232
- test('the planet forms from the answers: a seed, a formed planet, the protective ring, the core material', () => {
233
- const w = boot();
234
- w.run('phys()');
235
- const src = () => w.$('physPlanet').getAttribute('src');
236
- w.state.physics.q = 1; w.state.physics.answers.project_state = 'new';
237
- w.run('physPaintStage()');
238
- assert.equal(src(), '/assets/world/r2-chrome-blob-one-cut.webp', 'a seed');
239
- w.state.physics.answers.project_state = 'existing';
240
- w.run('physPaintStage()');
241
- assert.equal(src(), '/assets/world/r2-chrome-blobs-set-cut.webp', 'terrain already formed');
242
- assert.equal(w.$('physGuard').hidden, true);
243
- w.state.physics.q = 3; w.state.physics.answers.minors_on_board = true;
244
- w.run('physPaintStage()');
245
- assert.equal(w.$('physGuard').hidden, false, 'the protective ring');
246
- w.state.physics.q = 4;
247
- for (const [entity, plate] of [['intent-solo', 'r3-frost-blob-cut'], ['for-profit', 'r3-gold-blob-cut'], ['nonprofit', 'r3-obsidian-blob-cut']]) {
248
- w.state.physics.answers.entity = entity;
249
- w.run('physPaintStage()');
250
- assert.equal(src(), `/assets/world/${plate}.webp`, entity);
251
- }
252
- for (const plate of ['r3-frost-blob-cut', 'r3-gold-blob-cut', 'r3-obsidian-blob-cut', 'r2-chrome-blob-one-cut', 'r2-chrome-blobs-set-cut']) {
253
- assert.ok(fs.existsSync(path.join(LANDING, 'assets', 'world', `${plate}.webp`)), `${plate} is a world plate`);
261
+ test('the forming planet is gone: the night room stands behind the wizard instead (task 1004504)', () => {
262
+ for (const gone of ['physPaintStage', 'PHYS_MATERIAL', 'PHYS_CORE_WORD', 'PHYS_ORBIT', 'physAdvance', 'physNextQ', 'physPrevQ', 'physSummaryRows']) {
263
+ assert.ok(!HUB.includes(gone), `${gone} is gone`);
254
264
  }
265
+ assert.doesNotMatch(HUB, /id="(physPlanet|physGuard|physStage|panel-physics|physSummary)"/, 'and so is its markup');
266
+ assert.match(HUB, /<div class="wizRoom" id="wizRoom" data-screen="fork" aria-hidden="true">/, 'the room is decoration only');
267
+ const seed = HUB.match(/<span class="rmSeed"><img src="\/assets\/world\/([a-z0-9-]+)\.webp" alt=""><\/span>/);
268
+ assert.ok(seed, 'the room\'s seed is a world plate');
269
+ assert.ok(fs.existsSync(path.join(LANDING, 'assets', 'world', `${seed[1]}.webp`)), `${seed[1]} exists`);
255
270
  });
256
271
 
257
272
  test('a limit the table puts on an option shows its reason beside it, and a grey one is disabled', () => {
258
273
  const w = boot();
259
274
  const why = new El('why');
260
275
  const btn = new El('btn');
276
+ const fieldset = new El('physExisting');
261
277
  w.sandbox.root.querySelector = (sel) => (sel.includes('data-why="existing_work"') ? why : null);
262
- w.sandbox.root.querySelectorAll = (sel) => (sel.includes('data-phys="entity"][data-val="intent-solo"') ? [btn] : []);
278
+ w.sandbox.root.querySelectorAll = (sel) => (sel.includes('data-phys="entity"][data-val="intent-solo"') ? [btn]
279
+ : sel.includes('[data-limit="existing_work"]') ? [fieldset] : []);
263
280
  w.run(`physLimits = [
264
281
  { option: 'existing_work', effect: 'hide', reason: 'Your planet is new, so there is nothing built yet to describe.' },
265
282
  { option: 'entity:intent-solo', effect: 'grey', reason: 'Because.' }
266
283
  ]; physApplyLimits();`);
267
284
  assert.equal(why.hidden, false);
268
285
  assert.match(why.textContent, /nothing built yet/);
286
+ assert.equal(fieldset.hidden, true, 'a question the table hides is hidden in place');
269
287
  assert.equal(btn.disabled, true);
270
288
  assert.equal(btn.title, 'Because.');
271
289
  });