@bongos/core 1.21.91 → 1.21.92
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 +39 -39
- package/docs/copy-inventory.md +357 -367
- package/docs/copy-registry.json +559 -641
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +15 -18
- package/docs/page-readings.json +1235 -918
- package/modules/public-landing/public/projects-hall-live.states.json +6 -35
- package/modules/public-landing/public/projects.html +1019 -983
- package/modules/public-landing/public/projects.probes.json +15 -18
- package/modules/public-landing/public/projects.states.json +22 -25
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/module-api.js +1 -1
- package/tests/onboard.mjs +8 -8
- package/tests/project_invite_ui.mjs +6 -5
- package/tests/projects_hub.mjs +77 -98
- package/tests/projects_hub_app_status.mjs +3 -3
- package/tests/projects_hub_app_step.mjs +8 -8
- package/tests/projects_hub_cloud_hop.mjs +29 -53
- package/tests/projects_hub_look.mjs +29 -39
- package/tests/projects_hub_module_picker.mjs +32 -39
- package/tests/projects_hub_pre_uat.mjs +11 -14
- package/tests/wizard_demo.mjs +30 -25
- package/tests/wizard_draft_resume.mjs +24 -32
- package/tests/wizard_front_door.mjs +91 -243
- package/tests/wizard_intent_resume.mjs +28 -48
- package/tests/wizard_one_page.mjs +201 -0
- package/tests/wizard_physics.mjs +46 -41
- package/tests/wizard_physics_more.mjs +71 -106
- package/tests/wizard_ship_mode.mjs +1 -1
- package/tests/wizard_six_screens.mjs +0 -129
package/tests/projects_hub.mjs
CHANGED
|
@@ -283,36 +283,28 @@ test('/create is absorbed — the wizard lives here and the route only redirects
|
|
|
283
283
|
const server = fs.readFileSync(path.join(ROOT, 'src', 'platform-server.js'), 'utf8');
|
|
284
284
|
assert.match(server, /res\.redirect\(302, '\/projects\?view=new'/,
|
|
285
285
|
'/create must 302 into the hub (302, not 301: the URL is an OAuth return target in the wild)');
|
|
286
|
-
// the
|
|
287
|
-
// folded in are gone rather than left behind as dead panels
|
|
288
|
-
for (const id of ['panel-gate', 'panel-
|
|
286
|
+
// the one page (task 1004648): the gate, then the page and its three parts, all real
|
|
287
|
+
// markup, and the screens they folded in are gone rather than left behind as dead panels
|
|
288
|
+
for (const id of ['panel-gate', 'panel-page', 'part-project', 'part-run', 'panel-done']) {
|
|
289
289
|
assert.match(HUB, new RegExp(`id="${id}"`), `the wizard's ${id} ships as real markup (C7)`);
|
|
290
290
|
}
|
|
291
|
-
for (const id of ['panel-terms', 'panel-physics', 'panel-name', 'panel-type', 'panel-detail', 'panel-modules', 'panel-home', 'panel-address', 'panel-app'
|
|
292
|
-
|
|
291
|
+
for (const id of ['panel-terms', 'panel-physics', 'panel-name', 'panel-type', 'panel-detail', 'panel-modules', 'panel-home', 'panel-address', 'panel-app',
|
|
292
|
+
'panel-fork', 'panel-what', 'panel-checks', 'panel-review', 'panel-look']) {
|
|
293
|
+
assert.doesNotMatch(HUB, new RegExp(`id="${id}"`), `the retired ${id} is gone (tasks 1004504, 1004648)`);
|
|
293
294
|
}
|
|
294
295
|
});
|
|
295
296
|
|
|
296
|
-
test('the wizard
|
|
297
|
-
// The
|
|
298
|
-
//
|
|
297
|
+
test('the wizard has no steps left to couple: one page, no data-next forms, no per-panel Back (task 1004648)', () => {
|
|
298
|
+
// The old renumbering risk turned on validateStep(next - 1) and data-next forms; the one
|
|
299
|
+
// page has neither, so the map is the gate, the page, and the same page created.
|
|
299
300
|
const pm = SCRIPT.match(/var panels = \{([^}]*)\}/);
|
|
300
301
|
assert.ok(pm, 'the panels map must exist');
|
|
301
302
|
const map = {};
|
|
302
303
|
for (const m of pm[1].matchAll(/(\d+):\s*'([a-z-]+)'/g)) map[Number(m[1])] = m[2];
|
|
303
|
-
|
|
304
|
-
assert.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const sec = before.lastIndexOf('id="panel-');
|
|
308
|
-
assert.ok(sec !== -1, 'a data-next form lives inside a panel');
|
|
309
|
-
const id = HUB.slice(sec + 4).match(/^(panel-[a-z]+)/)[1];
|
|
310
|
-
assert.equal(id, map[Number(m[1]) - 1],
|
|
311
|
-
`data-next="${m[1]}" must live in panels[${Number(m[1]) - 1}] — validateStep(next-1) guards that panel`);
|
|
312
|
-
}
|
|
313
|
-
for (const m of HUB.matchAll(/data-back="(\d+)"/g)) {
|
|
314
|
-
assert.ok(map[Number(m[1])], `data-back="${m[1]}" must point at a mapped panel`);
|
|
315
|
-
}
|
|
304
|
+
assert.deepEqual(map, { 0: 'panel-gate', 1: 'panel-page', 2: 'panel-page' });
|
|
305
|
+
assert.deepEqual([...HUB.matchAll(/data-next="(\d+)"/g)], [], 'no form walks to a next step');
|
|
306
|
+
assert.deepEqual([...HUB.matchAll(/data-back="(\d+)"/g)], [], 'and nothing walks back');
|
|
307
|
+
assert.doesNotMatch(SCRIPT, /function validateStep\(/, 'no per-step validator is left');
|
|
316
308
|
// the numbered rail is gone (task 1004504): no beat is left to drift from the map
|
|
317
309
|
const beats = [...HUB.matchAll(/data-step="(\d+)"/g)].map((x) => Number(x[1]));
|
|
318
310
|
assert.deepEqual(beats, [], 'no rail beat is left on the page');
|
|
@@ -662,59 +654,49 @@ test('every per-step hook in show() still points at the panel it belongs to (ren
|
|
|
662
654
|
const STEP = {};
|
|
663
655
|
for (const m of map.matchAll(/(\d+):\s*'([a-z-]+)'/g)) STEP[m[2]] = Number(m[1]);
|
|
664
656
|
const show = SCRIPT.slice(SCRIPT.indexOf('function show(step, moveFocus)'));
|
|
665
|
-
const endAt = show.search(/\n
|
|
666
|
-
assert.ok(endAt > 0, 'show()
|
|
657
|
+
const endAt = show.search(/\r?\n {4}\/\* ── the room's sky/);
|
|
658
|
+
assert.ok(endAt > 0, 'show() and paintPage() end where the room\'s sky begins — re-point this slice if it moved');
|
|
667
659
|
const body = show.slice(0, endAt);
|
|
668
660
|
|
|
669
|
-
// task
|
|
670
|
-
// entry
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
assert.
|
|
674
|
-
assert.match(
|
|
675
|
-
assert.match(
|
|
676
|
-
assert.match(
|
|
677
|
-
assert.match(
|
|
678
|
-
assert.match(
|
|
679
|
-
assert.
|
|
680
|
-
|
|
681
|
-
assert.
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
'the look and the recap paint on the look screen, the one Create sits on');
|
|
685
|
-
// the room behind the wizard follows the screen, through one map keyed like panels
|
|
661
|
+
// task 1004648: every old step's entry hook lives in ONE block, paintPage, run on every
|
|
662
|
+
// entry to the page — the rows host the old steps' controls, all on one page
|
|
663
|
+
assert.ok(body.includes('if (step === 1) paintPage();'), 'the page (step 1) paints on entry');
|
|
664
|
+
const page = (body.match(/function paintPage\(\) \{([\s\S]*?)\r?\n {4}\}/) || [, ''])[1];
|
|
665
|
+
assert.ok(page, 'the page has its own entry block');
|
|
666
|
+
assert.match(page, /physLoadAccountOnce\(\);/, 'the quick checks offer the account\'s last answers');
|
|
667
|
+
assert.match(page, /paintChecks\(\);/, 'and paint');
|
|
668
|
+
assert.match(page, /syncHome\(\);/, 'the code row repaints its repo controls');
|
|
669
|
+
assert.match(page, /if \(authKnown\) ensureRepoAccess\(\);/, 'the repo probe fires on the page, and never before /me settles');
|
|
670
|
+
assert.match(page, /checkSlugAvailable\(\); renderAddrPanel\(\);/, 'the handle re-check fires on entry (N10)');
|
|
671
|
+
assert.match(page, /syncAppStep\(\); syncRenderSetup\(\);/, 'the hosting row repaints its choice');
|
|
672
|
+
assert.match(page, /renderModules\(\);/, 'the extras row paints the picker');
|
|
673
|
+
assert.match(page, /renderLook\(\);/, 'the look row builds its picker');
|
|
674
|
+
assert.match(page, /paintSetup\(\);\s*paintCreate\(\);/, 'and the rows, then Create, are painted');
|
|
675
|
+
// the room behind the wizard follows the page, through one map keyed like panels
|
|
686
676
|
assert.match(body, /\$\('wizRoom'\)\.setAttribute\('data-screen', SCREEN\[step\]\);/, 'show() writes the room\'s data-screen');
|
|
687
677
|
const screens = (SCRIPT.match(/var SCREEN = \{([^}]*)\}/) || [, ''])[1];
|
|
688
|
-
assert.deepEqual([...screens.matchAll(/(\d+):/g)].map((m) => Number(m[1])),
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
assert.ok(!body.includes(`if (step === ${STEP['panel-done']})`),
|
|
692
|
-
'the done panel is painted by the create, not by a show() hook');
|
|
678
|
+
assert.deepEqual([...screens.matchAll(/(\d+):/g)].map((m) => Number(m[1])), [0, 1, 2], 'SCREEN names every step the map holds, and nothing else');
|
|
679
|
+
// and part 03 claims no paint of its own here — renderDone runs from the create
|
|
680
|
+
assert.doesNotMatch(body, /renderDone\(\)/, 'part 03 is painted by the create, not by show()');
|
|
693
681
|
});
|
|
694
682
|
|
|
695
683
|
test('the About answers are OFFERED by every form and can be walked past (task 1002334; folded into the screens by task 1004504)', () => {
|
|
696
|
-
// the About step is gone (task 1004504): its sentence is the optional field
|
|
697
|
-
//
|
|
684
|
+
// the About step is gone (task 1004504): its sentence is the optional field of part 01
|
|
685
|
+
// (task 1004648), and its team shape is a choice on part 02's Team row
|
|
698
686
|
assert.doesNotMatch(HUB, /id="panel-detail"|id="skipDetail"|id="detailSkipNote"/, 'no About panel and no skip button are left');
|
|
699
|
-
const what = (HUB.match(/<
|
|
700
|
-
assert.ok(what, '
|
|
687
|
+
const what = (HUB.match(/<div class="part" id="part-project"[\s\S]*?\n {14}<\/div>\n/) || [''])[0];
|
|
688
|
+
assert.ok(what, 'part 01 ships as real markup');
|
|
701
689
|
assert.match(what, /<input type="text" id="f-desc"/, 'it asks what the owner is building — the publish gate half');
|
|
702
|
-
// optional, and
|
|
703
|
-
assert.match(what, /<span>In a sentence <small>\(optional
|
|
690
|
+
// optional, and what it is for is stated on the field itself
|
|
691
|
+
assert.match(what, /<span>In a sentence <small>\(optional, it goes on the public map\)<\/small><\/span>/,
|
|
704
692
|
'the field says it is optional and names what it is for (ADR 0182 D4), before it asks');
|
|
705
693
|
const team = (HUB.match(/<div class="setRow" data-row="team">[\s\S]*?<span class="acts">/) || [''])[0];
|
|
706
|
-
assert.match(team, /id="teamCards"/, 'and who is building it — what the recommendations read — on the Team
|
|
707
|
-
//
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
assert.
|
|
712
|
-
const fn = validate.slice(0, validate.indexOf('\n function ', 1));
|
|
713
|
-
const clause = (fn.match(new RegExp(`if \\(step === ${whatStep}\\) \\{([\\s\\S]*?)\\n \\}`)) || [, ''])[1];
|
|
714
|
-
assert.ok(clause, 'the What screen has a validation clause (name + kind)');
|
|
715
|
-
assert.match(clause, /state\.name\.trim\(\)/, 'the name is required');
|
|
716
|
-
assert.match(clause, /state\.projType/, 'the kind is required');
|
|
717
|
-
assert.doesNotMatch(clause, /description|teamShape/, 'the optional answers have no validation — Continue past an empty one is the point');
|
|
694
|
+
assert.match(team, /id="teamCards"/, 'and who is building it — what the recommendations read — on the Team row');
|
|
695
|
+
// Create waits only for a name and the tick: nothing may gate the sentence or the team
|
|
696
|
+
const why = (SCRIPT.match(/function createWhy\(\) \{([\s\S]*?)\r?\n {4}\}/) || [, ''])[1];
|
|
697
|
+
assert.match(why, /state\.name/, 'the name is required');
|
|
698
|
+
assert.match(why, /state\.termsAck/, 'and the tick');
|
|
699
|
+
assert.doesNotMatch(why, /description|teamShape|projType/, 'the optional answers — the kind included — never hold Create');
|
|
718
700
|
});
|
|
719
701
|
|
|
720
702
|
test('the About answers travel on the create request, and an empty one is OMITTED not blanked', () => {
|
|
@@ -725,12 +707,11 @@ test('the About answers travel on the create request, and an empty one is OMITTE
|
|
|
725
707
|
assert.match(SCRIPT, /detail: createDetail\(\),/);
|
|
726
708
|
assert.match(SCRIPT, /if \(state\.teamShape\) d\.team_shape = state\.teamShape;\r?\n\s*return Object\.keys\(d\)\.length \? d : undefined;/,
|
|
727
709
|
'and an unanswered team shape is never guessed into the request');
|
|
728
|
-
// task
|
|
729
|
-
//
|
|
730
|
-
// cost is stated on its own field (pinned above)
|
|
710
|
+
// task 1004648: part 02 says only the answers the page PREFILLED, and the sentence is never
|
|
711
|
+
// prefilled, so it has no row there — what it is for is stated on its own field (pinned above)
|
|
731
712
|
assert.doesNotMatch(SCRIPT, /function renderReview\(|\$\('reviewList'\)/, 'no ledger review is left');
|
|
732
|
-
assert.match(SCRIPT, /var ROWS = \['code', 'address', '
|
|
733
|
-
'
|
|
713
|
+
assert.match(SCRIPT, /var ROWS = \['code', 'address', 'team', 'extras', 'rewards', 'app', 'run', 'ship', 'look'\];/,
|
|
714
|
+
'part 02\'s rows are the prefilled answers, in the mockup\'s order');
|
|
734
715
|
});
|
|
735
716
|
|
|
736
717
|
test('a renumbering of the wizard invalidates old drafts — the key carries the version', () => {
|
|
@@ -746,8 +727,9 @@ test('a renumbering of the wizard invalidates old drafts — the key carries the
|
|
|
746
727
|
// task 1004421 put the Look step between App and Review: the seventh
|
|
747
728
|
// task 1004418 put planet physics right after the fork: the eighth
|
|
748
729
|
// task 1004504 blended the thirteen into six screens: the ninth
|
|
749
|
-
|
|
750
|
-
assert.equal(
|
|
730
|
+
// task 1004648 put the whole flow on one page: the tenth
|
|
731
|
+
assert.equal(count, 3, 'gate, the page, and the page created — bump DRAFT_KEY when this changes');
|
|
732
|
+
assert.equal(key, 'cb-create-draft-v10', 'and the key moved with the one page');
|
|
751
733
|
});
|
|
752
734
|
|
|
753
735
|
test('every row lays the same five parts — the ledger has no ragged row', () => {
|
|
@@ -2201,7 +2183,7 @@ test('the wizard folds at one width: no rail fold is left, and no 900px rule is
|
|
|
2201
2183
|
assert.match(STYLE, /@media \(max-width:840px\)\{\s*\.row2,\.choices\{grid-template-columns:1fr;\}[\s\S]*?\.typeCards\{grid-template-columns:1fr;\}/, 'the page\'s own wizard block still folds its bands at that same 840');
|
|
2202
2184
|
// the ledger review is gone (task 1004504); its rows stack at their own, narrower width
|
|
2203
2185
|
assert.doesNotMatch(STYLE, /\.review\{/, 'no .review ledger rule is left');
|
|
2204
|
-
assert.match(STYLE, /@media \(max-width:560px\)\{\s*\.setRow\{grid-template-columns:minmax\(0,1fr\);/, '
|
|
2186
|
+
assert.match(STYLE, /@media \(max-width:560px\)\{\s*\.setRow\{grid-template-columns:minmax\(0,1fr\) auto;grid-template-areas:"k acts" "v v";/, 'part 02\'s rows stack under 560, Change beside the label');
|
|
2205
2187
|
});
|
|
2206
2188
|
|
|
2207
2189
|
test('the wizard\'s own structures wear the world: hairline fields, cells of one band, the review as a ledger, the human steps as a numbered ledger, the standup strip as the rail (task 1003346)', () => {
|
|
@@ -2214,13 +2196,14 @@ test('the wizard\'s own structures wear the world: hairline fields, cells of one
|
|
|
2214
2196
|
assert.match(block, /\.tpill\[aria-pressed=true\]::before\{background:var\(--accent\);/, 'the chosen cell carries the dot');
|
|
2215
2197
|
assert.match(block, /\.choices\{display:grid;grid-template-columns:1fr 1fr;gap:0;border-top:1px solid var\(--rule\);/, 'start fresh / adopt are two cells of one band');
|
|
2216
2198
|
assert.match(block, /\.choice svg\{display:none;\}/, 'the dot is the mark, the icon leaves');
|
|
2217
|
-
// task
|
|
2218
|
-
assert.match(block, /\.setRows\{margin:
|
|
2219
|
-
assert.match(block, /\.setRow\{display:grid;grid-template-columns:
|
|
2199
|
+
// task 1004648: part 02 is one hairline column of rows to skim, not a ledger of li
|
|
2200
|
+
assert.match(block, /\.setRows\{margin:14px 0 0;border-top:1px solid var\(--rule\);\}/, 'part 02\'s rows hang from one hairline');
|
|
2201
|
+
assert.match(block, /\.setRow\{display:grid;grid-template-columns:108px minmax\(0,1fr\) auto;[^}]*border-bottom:1px solid var\(--rule\);\}/, 'each row is label · value · Change on its own hairline');
|
|
2220
2202
|
assert.doesNotMatch(block, /\.review( li)?\{/, 'the ledger\'s rules are gone with renderReview');
|
|
2221
|
-
assert.match(block, /\.humanSteps>li\{\s*display:grid;grid-template-columns:44px 1fr;/, 'the human steps
|
|
2203
|
+
assert.match(block, /\.humanSteps>li\{\s*display:grid;grid-template-columns:44px 1fr;/, 'the human steps keep the ledger grid');
|
|
2204
|
+
assert.match(block, /#panel-done \.humanSteps>li\{grid-template-columns:24px 1fr;/, 'which part 03 draws as the mockup\'s checklist: a dot, then the step');
|
|
2222
2205
|
assert.match(block, /\.humanSteps>li>\*\{grid-column:2;/, 'every child of a step sits in the text column, never under the counter (the sheet\'s bug)');
|
|
2223
|
-
assert.match(block,
|
|
2206
|
+
assert.match(block, /#panel-done \.humanSteps>li\.sdone::before\{content:"✓";/, 'a verified step wears the tick in the ok ink');
|
|
2224
2207
|
assert.match(block, /\.prog\{background:none;border:0;border-radius:0;padding:0;/, 'the standup strip is drawn as the rail');
|
|
2225
2208
|
assert.match(block, /\.prog \.stages li\.now \.sdot::after\{content:none;animation:none;\}/, 'no pulse on the rail');
|
|
2226
2209
|
assert.match(block, /\.doneHead \.tick\{display:none;\}/, 'the tick leaves; the rail says filed');
|
|
@@ -2229,13 +2212,12 @@ test('the wizard\'s own structures wear the world: hairline fields, cells of one
|
|
|
2229
2212
|
// the one exception is drawn, not chrome: the night room's moon glows in the window
|
|
2230
2213
|
// (task 1004504), and a control's halo is still an outline
|
|
2231
2214
|
const shadows = [...block.matchAll(/([^{}]*)\{[^}]*box-shadow:(?!none)/g)].map((m) => m[1].trim());
|
|
2232
|
-
assert.deepEqual(shadows, ['#view-new .rmMoon'], 'no box shadow in the wizard block but the room\'s moon');
|
|
2233
|
-
// no
|
|
2234
|
-
|
|
2235
|
-
assert.
|
|
2236
|
-
assert.match(HUB, /<div class="
|
|
2237
|
-
assert.match(HUB, /<
|
|
2238
|
-
assert.match(HUB, /<h2 class="stepsHead" id="doneStepsHead">Your steps — the parts only you can play<\/h2>/, 'the steps heading is the label face by class, not an inline style (the id is where a skipped invite step hands focus on, task 1002346)');
|
|
2215
|
+
assert.deepEqual(shadows, ['#view-new .rmMoon', '#view-new[data-sky="day"] .rmMoon'], 'no box shadow in the wizard block but the room\'s moon, and its sun by day');
|
|
2216
|
+
// no Back at all any more (task 1004648): one page, and Create with its recap beside it
|
|
2217
|
+
assert.doesNotMatch(HUB, /data-back="|id="wizBack"/, 'no back door is left');
|
|
2218
|
+
assert.match(HUB, /<div class="wizTop">\s*<span class="wizEyebrow" id="wizEyebrow">New project<\/span>\s*<\/div>/, 'the eyebrow alone in the top row');
|
|
2219
|
+
assert.match(HUB, /<div class="createBar">\s*<button type="button" class="btn big" id="doCreate">Create my project<\/button>\s*<span class="wizRecap" id="wizRecap"><\/span>/, 'Create, with the one-line recap beside it');
|
|
2220
|
+
assert.match(HUB, /<h2 id="done-part-h"><span class="n">03<\/span>Connect GitHub <span class="state" id="ghState">opens once your project exists<\/span><\/h2>/, 'part 03 names itself and where it is');
|
|
2239
2221
|
// what is enqueued is unchanged (ADR 0111): the filing body the control plane reads
|
|
2240
2222
|
assert.match(SCRIPT, /body: JSON\.stringify\(\{\s*slug: state\.slug,/, 'the filing still carries the slug first');
|
|
2241
2223
|
assert.match(SCRIPT, /hosting_shape: 'cloud-host',/, 'the shape is unchanged');
|
|
@@ -2243,9 +2225,9 @@ test('the wizard\'s own structures wear the world: hairline fields, cells of one
|
|
|
2243
2225
|
assert.match(SCRIPT, /onboard_mode: state\.mode === 'adopt' \? 'adopt' : 'greenfield'/, 'the onboard mode is unchanged');
|
|
2244
2226
|
// the kit walks every step and files against the stub
|
|
2245
2227
|
const states = JSON.parse(fs.readFileSync(path.join(LANDING, 'projects.states.json'), 'utf8')).states;
|
|
2246
|
-
for (const s of ['
|
|
2228
|
+
for (const s of ['named', 'ready', 'demo', 'checks-filled', 'row-code', 'row-extras', 'look', 'create-blocked', 'done']) assert.ok(states['in-new-' + s], `the ${s} state has a kit state`);
|
|
2247
2229
|
const probes = JSON.parse(fs.readFileSync(path.join(LANDING, 'projects.probes.json'), 'utf8')).probes;
|
|
2248
|
-
assert.ok(probes.some((p) => /the wizard driver \(
|
|
2230
|
+
assert.ok(probes.some((p) => /the wizard driver \(task 1004648\b/.test(p.name) && p.steps.some((st) => st[1] === '#doCreate')), 'the wizard driver files a project end to end');
|
|
2249
2231
|
// a walk that clicks a panel the page no longer has stops dead at that click: every panel a
|
|
2250
2232
|
// state or probe names must be one of the screens (task 1004504 retired seven of them)
|
|
2251
2233
|
const live = new Set([...HUB.matchAll(/<section class="panel(?: on)?" id="(panel-[a-z]+)"/g)].map((m) => m[1]));
|
|
@@ -2316,20 +2298,17 @@ test('the extras picker ships as real markup on the review\'s Extras row and is
|
|
|
2316
2298
|
assert.match(EXTRAS_ROW, /id="modCards"/, 'the band the picker paints its rows into');
|
|
2317
2299
|
assert.match(EXTRAS_ROW, /class="typeCards"/, 'and it is the SAME band the type and team questions use — no new grammar');
|
|
2318
2300
|
assert.match(EXTRAS_ROW, /id="modCoreNote"/, 'the always-on core is stated on the row');
|
|
2319
|
-
// the
|
|
2320
|
-
//
|
|
2321
|
-
assert.match(HUB, /<div class="setRow" data-row="extras">[\s\S]*?<button type="button" class="rowChg" data-row-chg aria-expanded="false">
|
|
2301
|
+
// the suggestion is the server's own until the owner toggles (task 1004648): no row is
|
|
2302
|
+
// confirmed, so an untouched picker files no answer and the server applies the same bundle
|
|
2303
|
+
assert.match(HUB, /<div class="setRow" data-row="extras">[\s\S]*?<button type="button" class="rowChg" data-row-chg aria-expanded="false">Change<\/button>/,
|
|
2322
2304
|
'the row can be opened and changed');
|
|
2323
|
-
assert.
|
|
2324
|
-
|
|
2325
|
-
// confirming turns the shown set into an answer given; the row's validity never refuses it
|
|
2326
|
-
const confirm = (SCRIPT.match(/function confirmRow\(key\) \{([\s\S]*?)\n \}/) || [, ''])[1];
|
|
2327
|
-
assert.match(confirm, /if \(key === 'extras' && starterBundles\) state\.modules = pickedModules\(\);/,'confirming the row is the answer — once the catalog has landed, never an empty list in its place');
|
|
2305
|
+
assert.doesNotMatch(HUB, /id="reviewGo"/, 'and there is no confirm control to accept it with');
|
|
2306
|
+
assert.doesNotMatch(SCRIPT, /state\.modules = pickedModules\(\);/, 'nothing turns the suggestion into an answer the owner did not give');
|
|
2328
2307
|
const valid = (SCRIPT.match(/function rowValid\(key\) \{([\s\S]*?)\n \}/) || [, ''])[1];
|
|
2329
2308
|
assert.ok(valid, 'rowValid() must exist');
|
|
2330
2309
|
assert.doesNotMatch(valid, /'extras'/, 'the extras row has no check that can refuse it — any set, even none, is an answer');
|
|
2331
|
-
// and the kind is what the suggestion follows, so changing it
|
|
2332
|
-
assert.match(SCRIPT, /\/\* the suggested extras follow the kind, so
|
|
2310
|
+
// and the kind is what the suggestion follows, so changing it repaints the row
|
|
2311
|
+
assert.match(SCRIPT, /\/\* the suggested extras follow the kind, so their row repaints with it \*\/\s*renderModules\(\);\s*paintSetup\(\);/);
|
|
2333
2312
|
});
|
|
2334
2313
|
|
|
2335
2314
|
test('the picker\'s search ships as real markup, in the wizard\'s own field grammar (task 1002340)', () => {
|
|
@@ -2399,7 +2378,7 @@ test('the kit stub answers the picker\'s read, so the panel renders in the desig
|
|
|
2399
2378
|
const stub = fs.readFileSync(path.join(ROOT, 'modules', 'ui-design', 'kit', 'serve.js'), 'utf8');
|
|
2400
2379
|
assert.match(stub, /rest === '\/provisioning\/starter-bundles'/, 'the stub serves the picker\'s read');
|
|
2401
2380
|
const probes = JSON.parse(fs.readFileSync(path.join(LANDING, 'projects.probes.json'), 'utf8')).probes;
|
|
2402
|
-
assert.ok(probes.some((p) => /the wizard driver \(
|
|
2403
|
-
&& p.steps.some((st) => st[1] === '#
|
|
2404
|
-
'and the end-to-end driver
|
|
2381
|
+
assert.ok(probes.some((p) => /the wizard driver \(task 1004648\b/.test(p.name)
|
|
2382
|
+
&& p.steps.some((st) => st[1] === '#doCreate')),
|
|
2383
|
+
'and the end-to-end driver walks the page through to Create rather than stopping at it');
|
|
2405
2384
|
});
|
|
@@ -257,9 +257,9 @@ test('a repaint never lands on top of an open Retry — from this watch or a fre
|
|
|
257
257
|
});
|
|
258
258
|
|
|
259
259
|
test('both surfaces use it: the done panel and the project page', () => {
|
|
260
|
-
// the
|
|
261
|
-
assert.match(HUB, /
|
|
262
|
-
assert.match(HUB, /if \(box\) watchApp\(instanceId, box, function \(\) \{ return state\.step ===
|
|
260
|
+
// part 03 goes live on the created page, step 2 of the one-page flow (task 1004648)
|
|
261
|
+
assert.match(HUB, /var SCREEN = \{ 0: 'signin', 1: 'page', 2: 'done' \};/);
|
|
262
|
+
assert.match(HUB, /if \(box\) watchApp\(instanceId, box, function \(\) \{ return state\.step === 2; \}, 5000\);/);
|
|
263
263
|
assert.match(HUB, /<div id="doneApp" hidden><\/div>/);
|
|
264
264
|
assert.match(HUB, /<div class="card mSec" id="mAppCard" hidden>\n\s*<h2>Where its app runs<\/h2>\n\s*<div id="mApp"><\/div>/);
|
|
265
265
|
assert.match(HUB, /loadAppStatus\(inst\.id \|\| asked, inst\);/);
|
|
@@ -157,22 +157,22 @@ const ANSWERS = { [LOOKUP]: TWO, [LOOKUP + '#' + TEA_A]: SERVICES };
|
|
|
157
157
|
|
|
158
158
|
test('the row is real markup: Render and Decide later, Decide later the default', () => {
|
|
159
159
|
assert.ok(PANEL, 'the review carries a data-row="app" row');
|
|
160
|
-
assert.match(PANEL, /<span class="k">
|
|
160
|
+
assert.match(PANEL, /<span class="k">Hosting<\/span>/, 'the row is the mockup\'s Hosting (task 1004648)');
|
|
161
161
|
assert.match(PANEL, /<input type="radio" name="appPlatform" value="render" aria-describedby="appRenderWhat">/, 'Render can be picked (task 1004352)');
|
|
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(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">
|
|
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">Change<\/button>/, 'Change opens the row in place, and reads Done while open (task 1004648)');
|
|
166
166
|
assert.doesNotMatch(HUB, /id="panel-app"/, 'the old App panel is gone');
|
|
167
167
|
});
|
|
168
168
|
|
|
169
|
-
test("the row sits in
|
|
170
|
-
assert.match(HUB, /var panels = \{ 0: 'panel-gate', 1: 'panel-
|
|
171
|
-
assert.match(HUB, /var ROWS = \['code', 'address', '
|
|
169
|
+
test("the row sits in part 02, between Rewards and We run, and its checks are the old step's (task 1004648)", () => {
|
|
170
|
+
assert.match(HUB, /var panels = \{ 0: 'panel-gate', 1: 'panel-page', 2: 'panel-page' \};/);
|
|
171
|
+
assert.match(HUB, /var ROWS = \['code', 'address', 'team', 'extras', 'rewards', 'app', 'run', 'ship', 'look'\];/);
|
|
172
172
|
assert.doesNotMatch(HUB, /<li[^>]*data-step="\d+"/, 'no rail beat names the step any more');
|
|
173
|
-
assert.match(HUB, /
|
|
173
|
+
assert.match(HUB, /function paintPage\(\) \{[^}]*syncAppStep\(\); syncRenderSetup\(\); syncShipStep\(\);/, 'entering the page repaints the row, Render setup included');
|
|
174
174
|
assert.match(HUB, /if \(key === 'app'\) \{ syncAppStep\(\); syncRenderSetup\(\); \}/, 'opening the row repaints it too');
|
|
175
|
-
assert.match(HUB, /if \(key === 'app'\) return rowFixed\('app'\) \|\| validateRender\(\);/, '
|
|
175
|
+
assert.match(HUB, /if \(key === 'app'\) return rowFixed\('app'\) \|\| validateRender\(\);/, 'filing the row runs the Render check');
|
|
176
176
|
});
|
|
177
177
|
|
|
178
178
|
test('a fresh draft reads "decide later", and the Render setup stays hidden', () => {
|
|
@@ -517,5 +517,5 @@ test('the row names exactly what was chosen, prices included', () => {
|
|
|
517
517
|
});
|
|
518
518
|
|
|
519
519
|
test('Create hands the app to Render straight after the project is filed', () => {
|
|
520
|
-
assert.match(HUB, /show\(
|
|
520
|
+
assert.match(HUB, /show\(2, true\);\r?\n\s*sendRenderSetup\(data\.instance && data\.instance\.id\);/);
|
|
521
521
|
});
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
// The create wizard's done panel in two moments (task 1004578, from idea 1001205):
|
|
4
4
|
// WAIT — one status line, a thin bar and the cloud hop, a little game with one
|
|
5
5
|
// cloud per PUBLIC project on the platform — then ARRIVE — the steps only the
|
|
6
|
-
// founder can play, one
|
|
6
|
+
// founder can play, all on show as one checklist (part 03, task 1004648). The owner set the game's rules on a played
|
|
7
7
|
// prototype, and the nausea ones are the ones a refactor would quietly lose: the
|
|
8
8
|
// clouds stand still, the sky stands still, the view does not follow a jump.
|
|
9
9
|
//
|
|
10
10
|
// EXECUTED by the vm-slice idiom: momentFor/setMoment ride the same region the DNS
|
|
11
11
|
// test runs (renderProgress); the physics (hopStep), the score line (hopScore),
|
|
12
|
-
// the status sync (hopSync) and the
|
|
12
|
+
// the status sync (hopSync) and the checklist's state line (stepsPaint) are sliced function by
|
|
13
13
|
// function and run under narrow stubs. Every slice is asserted to exist, so a
|
|
14
14
|
// rename fails loudly instead of testing nothing. vm-born values are compared as
|
|
15
15
|
// primitives or through JSON.
|
|
@@ -238,66 +238,42 @@ test('the status line and the bar read what the strip says', () => {
|
|
|
238
238
|
assert.match(fn('setProgress'), /hopSync\(el\)/, 'every strip write syncs the line');
|
|
239
239
|
});
|
|
240
240
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
-
const sandbox = { $: (id) =>
|
|
241
|
+
// ── part 03's checklist (task 1004648): every step on show, the state line from it ──
|
|
242
|
+
function bootSteps(items, { step = 2, inFlight = false } = {}) {
|
|
243
|
+
const list = { children: items };
|
|
244
|
+
const ghState = { textContent: '' };
|
|
245
|
+
const sandbox = { $: (id) => ({ humanSteps: list, ghState })[id] || null, Array, state: { step }, manifestInFlight: () => inFlight };
|
|
246
246
|
vm.createContext(sandbox);
|
|
247
|
-
vm.runInContext(
|
|
248
|
-
return { sandbox, nodes };
|
|
249
|
-
}
|
|
250
|
-
function li(key, cls = []) { const e = el(cls); e.key = key; e.focus = () => {}; return e; }
|
|
251
|
-
|
|
252
|
-
test('the arrival shows one step at a time, opening on the first not yet done', () => {
|
|
253
|
-
const items = [li('github-repo', ['sdone']), li('oauth-app'), li('device-flow', ['slocked']), li('enter-hall', ['slocked'])];
|
|
254
|
-
const { sandbox, nodes } = bootSteps(items);
|
|
255
|
-
sandbox.stepsPaint(true);
|
|
256
|
-
assert.deepEqual(items.map((i) => i.classList.contains('cur')), [false, true, false, false]);
|
|
257
|
-
assert.equal(nodes.stepCount.textContent, 'Step 2 of 4');
|
|
258
|
-
assert.equal(items[1].attrs['data-n'], '02', 'the step on show carries its own number — a hidden step never counts');
|
|
259
|
-
assert.equal(nodes.stepBack.hidden, false);
|
|
260
|
-
assert.equal(nodes.stepNext.hidden, false);
|
|
261
|
-
// the checklist ticks itself: the step on show finishing moves the founder on
|
|
262
|
-
items[1].classList.add('sdone');
|
|
263
|
-
sandbox.stepsPaint();
|
|
264
|
-
assert.equal(nodes.stepCount.textContent, 'Step 3 of 4');
|
|
265
|
-
// once they page for themselves, their choice holds
|
|
266
|
-
sandbox.stepGo(-1);
|
|
267
|
-
items[2].classList.add('sdone');
|
|
247
|
+
vm.runInContext(fn('stepsPaint'), sandbox);
|
|
268
248
|
sandbox.stepsPaint();
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
});
|
|
249
|
+
return ghState.textContent;
|
|
250
|
+
}
|
|
251
|
+
function li(key, cls = []) { const e = el(cls); e.key = key; return e; }
|
|
273
252
|
|
|
274
|
-
test('
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
assert.equal(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
assert.
|
|
281
|
-
const done = bootSteps([li('github-repo'), li('oauth-app', ['sdone'])]);
|
|
282
|
-
done.sandbox.stepsPaint();
|
|
283
|
-
assert.equal(done.nodes.hopWhile.hidden, true, 'already wired: nothing to offer');
|
|
284
|
-
const opened = bootSteps([li('github-repo'), li('oauth-app', ['sdone'])], true);
|
|
285
|
-
opened.sandbox.stepsPaint();
|
|
286
|
-
assert.equal(opened.nodes.hopWhile.hidden, false, 'but an open one keeps its door so it can be closed');
|
|
253
|
+
test('part 03\'s state line reads the checklist: ready, how many done, waiting on GitHub, connected', () => {
|
|
254
|
+
assert.equal(bootSteps([li('github-repo'), li('oauth-app')], { step: 1 }), 'opens once your project exists', 'before Create');
|
|
255
|
+
assert.equal(bootSteps([li('github-repo'), li('oauth-app')]), 'ready');
|
|
256
|
+
assert.equal(bootSteps([li('github-repo', ['sdone']), li('oauth-app'), li('enter-hall', ['slocked'])]), '1 of 3 done');
|
|
257
|
+
assert.equal(bootSteps([li('github-repo', ['sdone']), li('oauth-app')], { inFlight: true }), 'waiting on GitHub…', 'a GitHub leg in flight says so');
|
|
258
|
+
assert.equal(bootSteps([li('github-repo', ['sdone']), li('oauth-app', ['sdone'])]), 'connected ✓');
|
|
259
|
+
assert.doesNotMatch(fn('stepsPaint'), /innerHTML|\.cur|data-n/, 'no paging, and no markup from strings');
|
|
287
260
|
});
|
|
288
261
|
|
|
289
262
|
// ── the markup and the sheet ────────────────────────────────────────────────────
|
|
290
|
-
test('the wait stands the strip
|
|
291
|
-
for (const id of ['hop', 'hopStatus', 'hopBarFill', 'hopCanvas', 'hopLeft', 'hopJump', 'hopRight', 'hopScore', '
|
|
292
|
-
assert.ok(HUB.includes(`id="${id}"`), `#${id} is baked into
|
|
263
|
+
test('the wait stands the strip and the later lines down, the checklist stays on show, and nothing is paged', () => {
|
|
264
|
+
for (const id of ['hop', 'hopStatus', 'hopBarFill', 'hopCanvas', 'hopLeft', 'hopJump', 'hopRight', 'hopScore', 'humanSteps', 'ghState']) {
|
|
265
|
+
assert.ok(HUB.includes(`id="${id}"`), `#${id} is baked into part 03`);
|
|
266
|
+
}
|
|
267
|
+
for (const id of ['hopWhile', 'hopSignin', 'stepPager', 'stepCount', 'stepBack', 'stepNext']) {
|
|
268
|
+
assert.ok(!HUB.includes(`id="${id}"`), `#${id} is gone: the checklist is on show the whole time (task 1004648)`);
|
|
293
269
|
}
|
|
294
270
|
const panel = HUB.slice(HUB.indexOf('id="panel-done"'), HUB.indexOf('</section>', HUB.indexOf('id="panel-done"')));
|
|
295
|
-
assert.ok(panel.indexOf('id="
|
|
296
|
-
'the
|
|
271
|
+
assert.ok(panel.indexOf('id="humanSteps"') < panel.indexOf('id="hop"') && panel.indexOf('id="hop"') < panel.indexOf('id="wizInvite"'),
|
|
272
|
+
'the checklist first, then the hop, then the invite');
|
|
297
273
|
assert.match(panel, /id="hopStatus" role="status" aria-live="polite"/, 'the line is the live region while the strip is hidden');
|
|
298
274
|
assert.match(STYLE, /#panel-done\[data-moment="wait"\] #liveProgress,/);
|
|
299
|
-
assert.
|
|
300
|
-
assert.
|
|
275
|
+
assert.doesNotMatch(STYLE, /#panel-done\[data-moment="wait"\][^{]*#humanSteps\{display:none;\}/, 'the wait no longer hides the checklist');
|
|
276
|
+
assert.doesNotMatch(STYLE, /humanSteps>li:not\(\.cur\)/, 'and the arrival does not page it');
|
|
301
277
|
assert.match(STYLE, /#panel-done\[data-moment="arrive"\] #liveProgress \.stages\{display:none;\}/, 'the arrival keeps the note with the address, not the dots');
|
|
302
|
-
assert.match(STYLE,
|
|
278
|
+
assert.match(STYLE, /#panel-done \.humanSteps>li\.sdone::before\{content:"✓";/, 'a verified step wears the tick');
|
|
303
279
|
});
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// • the previews paint the REAL looks: every colour lookKit carries equals its
|
|
6
6
|
// pack.json, and the night shares equal src/night-tint.js — a preview that drifted
|
|
7
7
|
// from the hall would promise a look the hall does not wear;
|
|
8
|
-
// • the
|
|
9
|
-
// draft
|
|
8
|
+
// • the look is part 02's Look row on the one page (task 1004648), above Create,
|
|
9
|
+
// draft v10, no rail, and the kit pictures the row and "make your own";
|
|
10
10
|
// • EXECUTED by the vm-slice idiom: the picker offers the four looks and "make your
|
|
11
11
|
// own", shows the accent only for your own, shows the ADJUSTED colour in the
|
|
12
12
|
// preview and says so, paints a black night when the tint is off, refuses an SVG
|
|
@@ -105,41 +105,31 @@ test('every colour the previews paint is its pack\'s, and the night shares are t
|
|
|
105
105
|
assert.equal(kit.NIGHT.card, '#101012');
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
test('the
|
|
109
|
-
// task
|
|
110
|
-
//
|
|
111
|
-
assert.match(HUB, /
|
|
112
|
-
assert.match(HUB, /var DRAFT_KEY = 'cb-create-draft-
|
|
108
|
+
test('the look is part 02\'s Look row: Change opens the picker in place, above Create on the one page (task 1004648)', () => {
|
|
109
|
+
// task 1004648 put the whole flow on one page; the look is the summary's last row, so it
|
|
110
|
+
// is chosen beside everything else, and Create stands under it
|
|
111
|
+
assert.match(HUB, /var panels = \{ 0: 'panel-gate', 1: 'panel-page', 2: 'panel-page' \};/);
|
|
112
|
+
assert.match(HUB, /var DRAFT_KEY = 'cb-create-draft-v10';/);
|
|
113
113
|
assert.doesNotMatch(HUB, /<li[^>]*data-step="\d+"/, 'no rail beat is left on the page');
|
|
114
|
-
assert.doesNotMatch(HUB, /id="panel-
|
|
115
|
-
const
|
|
116
|
-
assert.match(
|
|
117
|
-
assert.
|
|
118
|
-
assert.match(
|
|
119
|
-
assert.
|
|
120
|
-
assert.match(
|
|
121
|
-
assert.
|
|
122
|
-
assert.match(HUB, /if \(step === 5\) \{ renderLook\(\); renderRecap\(\); \}/);
|
|
114
|
+
assert.doesNotMatch(HUB, /id="panel-look"|id="look-h"/, 'no Look screen any more');
|
|
115
|
+
const row = (HUB.match(/<div class="setRow" data-row="look">[\s\S]*?\n {18}<\/div>\n/) || [''])[0];
|
|
116
|
+
assert.match(row, /<span class="k">Look<\/span>/);
|
|
117
|
+
assert.match(row, /<div class="opts" hidden>\s*<div class="lookPick" id="wizLook"><\/div>/, 'Change opens the shared picker in place');
|
|
118
|
+
assert.match(row, /<span class="swDot" id="lookDot" aria-hidden="true"><\/span>/, 'the row wears the look\'s own accent');
|
|
119
|
+
assert.ok(HUB.indexOf('id="wizLook"') < HUB.indexOf('id="doCreate"'), 'the picker sits above Create');
|
|
120
|
+
assert.match(HUB, /if \(key === 'look'\) renderLook\(\);/, 'opening the row builds or repaints the picker');
|
|
121
|
+
assert.match(HUB, /lookKit\.buildLookPicker\(\$\('wizLook'\), state\.look, function \(\) \{ saveDraft\(\); paintSetup\(\); \}, 'wiz',/, 'a pick saves and repaints the row');
|
|
123
122
|
assert.doesNotMatch(fs.readFileSync(path.join(LANDING, 'assets', 'cosmos.css'), 'utf8'), /#view-new \.steps/, 'and no rail grid in cosmos.css');
|
|
124
123
|
});
|
|
125
124
|
|
|
126
|
-
test('
|
|
125
|
+
test('the kit has states on the Look row, the picker and "make your own"', () => {
|
|
127
126
|
const states = JSON.parse(fs.readFileSync(path.join(LANDING, 'projects.states.json'), 'utf8')).states;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
let past = 0;
|
|
131
|
-
for (const [name, steps] of walks) {
|
|
132
|
-
const at = (steps || []).findIndex((a) => a[1] === '#reviewGo');
|
|
133
|
-
if (at < 0) continue;
|
|
134
|
-
past++;
|
|
135
|
-
// the retired App and Look submits must not linger in a walk (they would stop it dead)
|
|
136
|
-
assert.ok(!steps.some((a) => a[1] === '#panel-app button[type=submit]' || a[1] === '#panel-look button[type=submit]'), `${name}: walks no retired step`);
|
|
137
|
-
const create = steps.findIndex((a) => a[1] === '#doCreate');
|
|
138
|
-
if (create >= 0) assert.ok(create > at, `${name}: files only after the review's Continue lands it on the Look screen`);
|
|
139
|
-
}
|
|
140
|
-
assert.ok(past >= 4, `sanity: the walks that leave the review were found (${past})`);
|
|
141
|
-
assert.deepEqual(states['in-new-look'].expect.visible.slice(0, 2), ['#panel-look', '#wizLook']);
|
|
127
|
+
assert.ok(states['in-new-look'].expect.visible.includes('#wizLook'));
|
|
128
|
+
assert.ok(states['in-new-look'].actions.some((a) => a[1] === '#reviewRows .setRow[data-row="look"] [data-row-chg]'), 'opened by its own Change');
|
|
142
129
|
assert.ok(states['in-new-look-custom'].actions.some((a) => a[1] === '#wizLook-custom'), 'a state pictures "make your own"');
|
|
130
|
+
for (const [name, s] of Object.entries(states)) {
|
|
131
|
+
for (const a of s.actions || []) assert.ok(!/panel-look|reviewGo|panel-app/.test(String(a[1])), `${name}: walks no retired screen`);
|
|
132
|
+
}
|
|
143
133
|
});
|
|
144
134
|
|
|
145
135
|
test('executed: the picker offers the four looks and your own, and the accent shows only for your own', () => {
|
|
@@ -239,17 +229,17 @@ test('executed: sendLook sends the look, then the logo — and a removed logo is
|
|
|
239
229
|
assert.deepEqual(calls.map((c) => [c.method, c.url.split('/').pop(), c.body]), [['PATCH', 'look', { look: 'grove', night_tint: 'on' }], ['DELETE', 'logo', null]]);
|
|
240
230
|
});
|
|
241
231
|
|
|
242
|
-
test('Create sends the look right after the project is filed, and the look is chosen on the
|
|
232
|
+
test('Create sends the look right after the project is filed, and the look is chosen on the page Create stands on', () => {
|
|
243
233
|
assert.match(HUB, /sendRenderSetup\(data\.instance && data\.instance\.id\);[^\n]*\n\s*sendLookSetup\(data\.instance && data\.instance\.id\);/);
|
|
244
234
|
assert.match(HUB, /lookKit\.sendLook\(instanceId, state\.look \|\| defaultLook\(\)\)/);
|
|
245
235
|
assert.match(HUB, /function defaultLook\(\) \{ return \{ look: 'chrome-world', nightTint: true, accent: '#b48cf2', logo: null \}; \}/, 'the default is the platform\'s look with the tint ON (owner D2)');
|
|
246
|
-
// the
|
|
247
|
-
//
|
|
248
|
-
assert.doesNotMatch(HUB, /function lookRow\(|\['Look', lookRow\(\)\]/, 'no
|
|
249
|
-
const
|
|
250
|
-
assert.ok(
|
|
251
|
-
assert.match(HUB, /<p class="hint" id="doneLook" role="status" hidden><\/p>/, 'a refused look is said
|
|
252
|
-
assert.equal((HUB.match(/look: null, termsAck: false
|
|
236
|
+
// the picker and Create share one page (task 1004648), so the look being filed is the one
|
|
237
|
+
// the Look row says, above the button
|
|
238
|
+
assert.doesNotMatch(HUB, /function lookRow\(|\['Look', lookRow\(\)\]/, 'no second copy restates the look');
|
|
239
|
+
const page = (HUB.match(/<section class="panel on" id="panel-page"[\s\S]*?\n {10}<\/section>/) || [''])[0];
|
|
240
|
+
assert.ok(page.indexOf('id="wizLook"') > -1 && page.indexOf('id="wizLook"') < page.indexOf('id="doCreate"'), 'the picker sits above Create on the same page');
|
|
241
|
+
assert.match(HUB, /<p class="hint" id="doneLook" role="status" hidden><\/p>/, 'a refused look is said in part 03');
|
|
242
|
+
assert.equal((HUB.match(/look: null, termsAck: false \};/g) || []).length, 2, 'the draft carries the look (and its reset clears it)');
|
|
253
243
|
});
|
|
254
244
|
|
|
255
245
|
test('the manage page has "Its look", read once per project — never on the minute tick', () => {
|