@bongos/core 1.21.105 → 1.21.107
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 +88 -58
- 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 +6 -1
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/api/openapi.json +91 -2
- package/docs/api-reference.md +5 -3
- package/docs/copy-inventory.md +368 -366
- package/docs/copy-registry.json +475 -457
- package/docs/design/project-startup-direction.md +14 -0
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +10 -3
- package/docs/page-readings.json +1023 -977
- package/migrations/core_275_claims_auto_pickup.sql +24 -0
- package/modules/government/decision-rules.js +1 -0
- package/modules/lifecycle/autopickup.js +94 -0
- package/modules/lifecycle/db-claims.js +18 -4
- package/modules/lifecycle/routes/claims.js +12 -1
- package/modules/lifecycle/routes/project-settings.js +59 -0
- package/modules/public-landing/public/projects-hall-live.states.json +3 -2
- package/modules/public-landing/public/projects-setup.states.json +91 -0
- package/modules/public-landing/public/projects.html +535 -804
- package/modules/public-landing/public/projects.probes.json +3 -3
- package/modules/public-landing/public/projects.states.json +1 -1
- package/modules/ui-design/kit/fixtures/provisioning-instances-hall-live.json +22 -2
- package/modules/ui-design/kit/fixtures/provisioning-instances-setup.json +195 -0
- package/modules/ui-design/kit/serve.js +22 -4
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/gds/government-walk.js +1 -0
- package/scripts/gds/run-unit-tests.js +3 -0
- package/src/bongos/project-settings.js +79 -0
- package/src/module-api.js +8 -1
- package/tests/autopickup_cap.mjs +236 -0
- package/tests/autopickup_cap_db.mjs +115 -0
- package/tests/cosmos_token_contrast.mjs +10 -18
- package/tests/github_handback_any_device.mjs +4 -3
- package/tests/github_one_trip.mjs +45 -47
- package/tests/github_org_account.mjs +2 -2
- package/tests/github_pick_up.mjs +40 -29
- package/tests/government_act_alone_buildings.mjs +1 -0
- package/tests/onboard.mjs +13 -7
- package/tests/project_invite_ui.mjs +6 -3
- package/tests/project_modules_ui.mjs +3 -3
- package/tests/projects_hub.mjs +8 -9
- package/tests/projects_hub_cloud_hop.mjs +77 -125
- package/tests/projects_hub_dns_ready.mjs +42 -51
- package/tests/projects_hub_pre_uat.mjs +68 -69
- package/tests/public_landing_projects.mjs +45 -102
- package/tests/setup_status_facts.mjs +400 -0
- package/tests/wizard_github_feedback.mjs +90 -87
- package/tests/wizard_one_page.mjs +11 -9
- package/tests/wizard_auto_open.mjs +0 -94
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
//
|
|
14
14
|
// Four layers, all DB-free and network-free:
|
|
15
15
|
// 1. manifest-truth.js — the effective status and how far a failure got (PURE).
|
|
16
|
-
// 2.
|
|
16
|
+
// 2. part 03's GitHub facts (ghFacts, task 1004683), EXECUTED by the vm-slice idiom:
|
|
17
|
+
// the words and the one action for each stage.
|
|
17
18
|
// 3. the reload path, EXECUTED: the seed from the instance read and the wait timer.
|
|
18
19
|
// 4. the project's own sign-in door with nothing configured: a page in words.
|
|
19
20
|
//
|
|
@@ -69,130 +70,132 @@ test('publicGithubApp: effective status, failure only on error, started_at, and
|
|
|
69
70
|
|
|
70
71
|
// ── 2. the words for each stage ─────────────────────────────────────────────
|
|
71
72
|
|
|
72
|
-
//
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const RENDER = SCRIPT.slice(
|
|
73
|
+
// part 03's facts (task 1004683): the pure half of the facts region, run as is
|
|
74
|
+
const FS = SCRIPT.indexOf(' function ghStep(key) {');
|
|
75
|
+
const FE = SCRIPT.indexOf(' /* ── drawing it: nodes and textContent only', FS);
|
|
76
|
+
const RENDER = SCRIPT.slice(FS, FE);
|
|
76
77
|
|
|
77
|
-
test('the GitHub
|
|
78
|
-
assert.ok(
|
|
79
|
-
assert.match(RENDER, /function
|
|
78
|
+
test('the GitHub facts region is still findable (a rename must fail loudly)', () => {
|
|
79
|
+
assert.ok(FS > 0 && FE > FS, 'both slice boundaries found');
|
|
80
|
+
assert.match(RENDER, /function ghFacts\(now\)/);
|
|
80
81
|
});
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
// every GitHub fact for one reading of the attempt, as the founder reads it
|
|
84
|
+
function stageAll(live, { tab = null } = {}) {
|
|
83
85
|
const sandbox = {
|
|
84
|
-
live: { manifest: null, manifestMsg: '', manifestCode: '', manifestFailure: null, appSlug: null, setup: null, installUrl: null, instStatus: 'active', addrReady: true, pickUp: null, verifying: false, askedPlatform: false, ...live },
|
|
86
|
+
live: { manifest: null, manifestMsg: '', manifestCode: '', manifestFailure: null, appSlug: null, setup: null, installUrl: null, instStatus: 'active', addrReady: true, pickUp: null, verifying: false, askedPlatform: false, inst: null, readAt: Date.now(), gone: '', repo: null, ...live },
|
|
85
87
|
state: { name: 'Mercury', slug: 'mercury' },
|
|
86
|
-
autoOpenArmed: false,
|
|
87
88
|
resolvedDomain: () => 'mercury.example.com',
|
|
88
89
|
apexBase: () => 'example.com',
|
|
89
|
-
|
|
90
|
-
GH_ICON: '',
|
|
91
|
-
manualOauthDetails: () => '<details>manual</details>',
|
|
92
|
-
projectPageLink: (t) => '<a>' + t + '</a>',
|
|
93
|
-
autoOpenTick: () => '<label>auto</label>',
|
|
94
|
-
progressMarkup: (now, done, note, cls) => `<prog now=${now} done=${done} cls="${cls}">${note}</prog>`,
|
|
90
|
+
addressReady: () => true,
|
|
95
91
|
};
|
|
96
92
|
vm.createContext(sandbox);
|
|
97
|
-
vm.runInContext(`${RENDER}\nghTabTrouble = ${JSON.stringify(tab)};\nvar out =
|
|
98
|
-
return sandbox.out
|
|
93
|
+
vm.runInContext(`${RENDER}\nghTabTrouble = ${JSON.stringify(tab)};\nvar out = JSON.stringify(ghFacts(Date.now()));`, sandbox);
|
|
94
|
+
return JSON.parse(sandbox.out).map((r) => ({
|
|
95
|
+
...r,
|
|
96
|
+
words: [r.title, [].concat(r.sub || []).map((p) => (typeof p === 'string' ? p : p.t)).join('')].join(' — '),
|
|
97
|
+
acts: [].concat(r.act || []),
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
function stage(live, { tab = null, key = 'approve' } = {}) {
|
|
101
|
+
return stageAll(live, { tab }).find((r) => r.key === key);
|
|
99
102
|
}
|
|
100
|
-
const startButton = /data-action="manifest-start"/;
|
|
101
103
|
const NEVER_A_CHORE = /\/contact|Contact us|delete it first|Developer settings → GitHub Apps\) so|Try again anyway|Start the setup again/;
|
|
102
104
|
|
|
103
|
-
test('pending:
|
|
104
|
-
const
|
|
105
|
-
assert.
|
|
106
|
-
assert.match(
|
|
107
|
-
assert.
|
|
108
|
-
assert.match(html, /class="linklike" data-action="reopen-github">No GitHub tab\? Reopen GitHub/, 'the way back is there from the first second, not after 90');
|
|
109
|
-
// the tab this page opened, seen from here (task 1004652)
|
|
105
|
+
test('pending: the founder\'s turn, says where a missing GitHub tab went, and reopens the SAME setup', () => {
|
|
106
|
+
const f = stage({ manifest: 'pending' });
|
|
107
|
+
assert.equal(f.state, 'you');
|
|
108
|
+
assert.match(f.words, /Waiting for you in the GitHub tab\./);
|
|
109
|
+
assert.deepEqual(f.acts, ['reopen'], 'no new-link button while GitHub holds this one (task 1004652); the way back is there from the first second');
|
|
110
110
|
const behind = stage({ manifest: 'pending' }, { tab: 'behind' });
|
|
111
|
-
assert.match(behind, /Your GitHub tab opened behind this window
|
|
112
|
-
assert.
|
|
111
|
+
assert.match(behind.words, /Your GitHub tab opened behind this window\./);
|
|
112
|
+
assert.deepEqual(behind.acts, ['reopen']);
|
|
113
113
|
const closed = stage({ manifest: 'pending' }, { tab: 'closed' });
|
|
114
|
-
assert.match(closed, /The GitHub tab closed before
|
|
115
|
-
assert.
|
|
116
|
-
assert.doesNotMatch(behind + closed, startButton);
|
|
114
|
+
assert.match(closed.words, /The GitHub tab closed before you approved, so nothing was made\. Reopen it to carry on\./);
|
|
115
|
+
assert.deepEqual(closed.acts, ['reopen']);
|
|
117
116
|
});
|
|
118
117
|
|
|
119
|
-
test('converting is the SAME
|
|
118
|
+
test('converting is the SAME fact as code_received — approved, keys being collected — never "waiting for you"', () => {
|
|
120
119
|
for (const m of ['code_received', 'converting']) {
|
|
121
|
-
const
|
|
122
|
-
assert.
|
|
123
|
-
assert.match(
|
|
124
|
-
assert.
|
|
125
|
-
assert.
|
|
120
|
+
const f = stage({ manifest: m });
|
|
121
|
+
assert.equal(f.state, 'now', `${m}: under way, not the founder's turn`);
|
|
122
|
+
assert.match(f.words, /Approved\. The platform is collecting the app’s keys\./, m);
|
|
123
|
+
assert.doesNotMatch(f.words, /Waiting for you/, m);
|
|
124
|
+
assert.deepEqual(f.acts, [], `${m}: nothing to press`);
|
|
126
125
|
}
|
|
127
126
|
});
|
|
128
127
|
|
|
129
|
-
test('sign-in unconfirmed: the app stays ticked, sign-in is
|
|
128
|
+
test('sign-in unconfirmed: the app stays ticked, sign-in is a stopped fact, and picking it up makes no second app', () => {
|
|
130
129
|
const live = { manifest: 'error', manifestFailure: 'sign_in_unconfirmed', appSlug: 'mercury-cloudbongos-com', pickUp: 'check' };
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
assert.
|
|
135
|
-
assert.match(signIn, /Your project didn’t confirm that sign-in works
|
|
136
|
-
assert.
|
|
137
|
-
assert.
|
|
138
|
-
assert.
|
|
139
|
-
assert.doesNotMatch(
|
|
140
|
-
// while the runner is asking again, the
|
|
141
|
-
const checking = stage({ ...live, pickUp: null, verifying: true }, {
|
|
142
|
-
assert.
|
|
143
|
-
assert.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
130
|
+
const all = stageAll(live);
|
|
131
|
+
const [approve, , signIn] = all;
|
|
132
|
+
assert.equal(approve.state, 'done', 'GitHub made the app and its keys are placed: that much is true');
|
|
133
|
+
assert.equal(signIn.state, 'bad');
|
|
134
|
+
assert.match(signIn.words, /Your project didn’t confirm that sign-in works\./);
|
|
135
|
+
assert.ok(signIn.acts.includes('ask-platform'), 'a project that still will not answer goes to the platform team from here');
|
|
136
|
+
assert.equal(all.flatMap((r) => r.acts).filter((a) => /^pick-up/.test(a)).length, 1, 'one "Pick up where it stopped", on the first stopped fact');
|
|
137
|
+
assert.ok(!all.some((r) => r.acts.includes('connect')), 'no new link: it would make a second app');
|
|
138
|
+
assert.doesNotMatch(all.map((r) => r.words).join('\n'), NEVER_A_CHORE);
|
|
139
|
+
// while the runner is asking again, the fact says so and offers no button that repeats it
|
|
140
|
+
const checking = stage({ ...live, pickUp: null, verifying: true }, { key: 'sign_in' });
|
|
141
|
+
assert.equal(checking.state, 'now');
|
|
142
|
+
assert.match(checking.words, /Asking your project again, with the app GitHub already made\./);
|
|
143
|
+
assert.deepEqual(checking.acts, []);
|
|
144
|
+
// sent: the fact says the platform team has it
|
|
145
|
+
const sent = stage({ ...live, askedPlatform: true }, { key: 'sign_in' });
|
|
146
|
+
assert.match(sent.words, /Sent\. The platform team has it and finishes it from here\./);
|
|
147
|
+
assert.ok(!sent.acts.includes('ask-platform'), 'sent once');
|
|
148
|
+
const failed = stage({ ...live, askedPlatform: 'failed' }, { key: 'sign_in' });
|
|
149
|
+
assert.match(failed.words, /That didn’t send\. Try again\./);
|
|
150
|
+
assert.ok(failed.acts.includes('ask-platform'));
|
|
147
151
|
});
|
|
148
152
|
|
|
149
153
|
test('approved, conversion stopped with its code still young: pick it up, no second app', () => {
|
|
150
|
-
const
|
|
151
|
-
assert.
|
|
152
|
-
assert.match(
|
|
153
|
-
assert.
|
|
154
|
-
assert.doesNotMatch(
|
|
155
|
-
assert.doesNotMatch(html, NEVER_A_CHORE);
|
|
154
|
+
const f = stage({ manifest: 'error', manifestFailure: 'finish_failed', pickUp: 'convert' });
|
|
155
|
+
assert.equal(f.state, 'bad');
|
|
156
|
+
assert.match(f.words, /GitHub approved the app, and the platform stopped before it finished collecting the app’s keys\./);
|
|
157
|
+
assert.deepEqual(f.acts, ['pick-up']);
|
|
158
|
+
assert.doesNotMatch(f.words, NEVER_A_CHORE);
|
|
156
159
|
});
|
|
157
160
|
|
|
158
161
|
test('approved but the keys never reached us: the platform team, sent from the page — never a GitHub chore', () => {
|
|
159
|
-
const
|
|
160
|
-
assert.match(
|
|
161
|
-
assert.
|
|
162
|
-
assert.doesNotMatch(
|
|
163
|
-
assert.doesNotMatch(
|
|
164
|
-
assert.doesNotMatch(html, /Nothing was changed on GitHub/);
|
|
162
|
+
const f = stage({ manifest: 'error', manifestFailure: 'finish_failed', pickUp: 'operator' });
|
|
163
|
+
assert.match(f.words, /GitHub made the app, but its keys never reached your project/);
|
|
164
|
+
assert.deepEqual(f.acts, ['ask-platform'], 'a new link would leave the founder with two apps');
|
|
165
|
+
assert.doesNotMatch(f.words, NEVER_A_CHORE);
|
|
166
|
+
assert.doesNotMatch(f.words, /Nothing was changed on GitHub/);
|
|
165
167
|
});
|
|
166
168
|
|
|
167
|
-
test('the install the runner stopped checking for: the mockup’s
|
|
168
|
-
const
|
|
169
|
-
assert.
|
|
170
|
-
assert.match(
|
|
171
|
-
assert.
|
|
172
|
-
|
|
173
|
-
|
|
169
|
+
test('the install the runner stopped checking for: the mockup’s words, and a pick-up that opens the SAME app’s install', () => {
|
|
170
|
+
const f = stage({ manifest: 'done', appSlug: 'mercury-app', installUrl: 'https://github.com/apps/mercury-app/installations/new', pickUp: 'check' }, { key: 'install' });
|
|
171
|
+
assert.equal(f.state, 'bad');
|
|
172
|
+
assert.match(f.words, /GitHub stopped before the app was on your repo\./);
|
|
173
|
+
assert.deepEqual(f.acts, ['pick-up-link']);
|
|
174
|
+
assert.equal(f.href, 'https://github.com/apps/mercury-app/installations/new');
|
|
175
|
+
// still being checked: the founder's turn on GitHub, no stop
|
|
176
|
+
assert.equal(stage({ manifest: 'done', appSlug: 'mercury-app', pickUp: null, verifying: true }, { key: 'install' }).state, 'you');
|
|
174
177
|
});
|
|
175
178
|
|
|
176
179
|
test('device flow after the runner stopped checking: "Check again", once the install has ticked', () => {
|
|
177
180
|
const setup = { approve: 'done', install: 'done', sign_in: 'done', device_flow: 'later', connected: true };
|
|
178
|
-
|
|
179
|
-
assert.
|
|
180
|
-
assert.
|
|
181
|
-
|
|
181
|
+
assert.deepEqual(stage({ manifest: 'done', appSlug: 'mercury-app', setup, pickUp: 'check' }, { key: 'device_flow' }).acts, ['check-again']);
|
|
182
|
+
assert.deepEqual(stage({ manifest: 'done', appSlug: 'mercury-app', setup, pickUp: null, verifying: true }, { key: 'device_flow' }).acts, []);
|
|
183
|
+
assert.deepEqual(stage({ manifest: 'done', appSlug: 'mercury-app', setup: { ...setup, install: 'now', connected: false }, pickUp: 'check' }, { key: 'device_flow' }).acts, [],
|
|
184
|
+
'the install fact carries the action until it ticks');
|
|
182
185
|
});
|
|
183
186
|
|
|
184
187
|
test('a failure before GitHub (or a refused start) keeps the honest "nothing changed" line', () => {
|
|
185
|
-
assert.match(stage({ manifest: 'error', manifestFailure: 'not_approved' }), /Nothing was changed on GitHub/);
|
|
188
|
+
assert.match(stage({ manifest: 'error', manifestFailure: 'not_approved' }).words, /Nothing was changed on GitHub/);
|
|
186
189
|
const refused = stage({ manifest: 'error', manifestMsg: 'HTTP 500' });
|
|
187
|
-
assert.match(refused, /Nothing was changed on GitHub/);
|
|
188
|
-
assert.
|
|
189
|
-
assert.
|
|
190
|
+
assert.match(refused.words, /Nothing was changed on GitHub/);
|
|
191
|
+
assert.equal(refused.tech, 'HTTP 500', 'the route text folds under Technical details');
|
|
192
|
+
assert.deepEqual(refused.acts, ['connect']);
|
|
190
193
|
});
|
|
191
194
|
|
|
192
195
|
test('an expired link offers a fresh one', () => {
|
|
193
|
-
const
|
|
194
|
-
assert.match(
|
|
195
|
-
assert.
|
|
196
|
+
const f = stage({ manifest: 'expired' });
|
|
197
|
+
assert.match(f.words, /That setup link expired/);
|
|
198
|
+
assert.deepEqual(f.acts, ['connect']);
|
|
196
199
|
});
|
|
197
200
|
|
|
198
201
|
// ── 3. the reload path ──────────────────────────────────────────────────────
|
|
@@ -201,7 +204,7 @@ const WS = SCRIPT.indexOf(' function manifestInFlight() {');
|
|
|
201
204
|
const WE = SCRIPT.indexOf(' function pollManifest(stateToken) {', WS);
|
|
202
205
|
const WAIT = SCRIPT.slice(WS, WE);
|
|
203
206
|
const SS = SCRIPT.indexOf(' var ga = data.github_app;');
|
|
204
|
-
const SE = SCRIPT.indexOf(' /* the checklist
|
|
207
|
+
const SE = SCRIPT.indexOf(' /* the server-verified checklist rides the same read (task 1004649)', SS);
|
|
205
208
|
const SEED = SCRIPT.slice(SS, SE);
|
|
206
209
|
|
|
207
210
|
test('the reload regions are still findable (a rename must fail loudly)', () => {
|
|
@@ -220,7 +223,7 @@ function reloaded(githubApp, now) {
|
|
|
220
223
|
live: { manifest: null, manifestFailure: null, appSlug: null },
|
|
221
224
|
manifestPollTimer: null, manifestStartedAt: 0, ghTabTrouble: null,
|
|
222
225
|
document: { hidden: false, hasFocus: () => true },
|
|
223
|
-
rememberManifest: () => {},
|
|
226
|
+
rememberManifest: () => {}, paintFacts: () => renders.push(1),
|
|
224
227
|
Date: { now: () => now, parse: Date.parse },
|
|
225
228
|
};
|
|
226
229
|
vm.createContext(sandbox);
|
|
@@ -112,7 +112,7 @@ test('nothing the live wizard does is lost: every capability in the direction ta
|
|
|
112
112
|
assert.match(row('run'), /data-val="hybrid" aria-pressed="false">Some of it<\/button>/, 'We run: "Some of it" reveals the service checklist');
|
|
113
113
|
has('the Shipping row', row('ship'), ['shipOpts']);
|
|
114
114
|
has('the Look row', row('look'), ['wizLook', 'lookDot']);
|
|
115
|
-
has('the page', PAGE, ['wizResume', 'termsAck', 'doCreate', 'wizRecap', 'createWhy', 'createAlert', '
|
|
115
|
+
has('the page', PAGE, ['wizResume', 'termsAck', 'doCreate', 'wizRecap', 'createWhy', 'createAlert', 'setupNow', 'homeFacts', 'ghFacts', 'wizInvite', 'done-h', 'enterGenesis']);
|
|
116
116
|
const ids = [...VIEW.matchAll(/\sid="([^"]+)"/g)].map((m) => m[1]);
|
|
117
117
|
assert.deepEqual(ids.filter((id, i) => ids.indexOf(id) !== i), [], 'moved, never copied: every id in the view is unique');
|
|
118
118
|
});
|
|
@@ -143,7 +143,7 @@ test('Create waits for a name and the terms tick only, and says why in place (ex
|
|
|
143
143
|
});
|
|
144
144
|
|
|
145
145
|
test('Create runs the screening stop before anything exists, and a row that cannot be filed opens with its reason', () => {
|
|
146
|
-
const create = slice('$(\'doCreate\').addEventListener(\'click\'', '/* ──
|
|
146
|
+
const create = slice('$(\'doCreate\').addEventListener(\'click\'', '/* ── part 03 after Create (task 1004683');
|
|
147
147
|
const order = ['createWhy()', 'firstInvalidRow()', 'physScreen()', 'ensureFreshRepo()', "fetch(API + '/provisioning/instances'"].map((s) => create.indexOf(s));
|
|
148
148
|
assert.ok(order.every((i, k) => i > 0 && (k === 0 || i > order[k - 1])), 'name and tick, then the rows, then the screen, then the repo, then the create');
|
|
149
149
|
assert.match(create, /if \(!ok\) \{ var stop = new Error\('stopped'\); stop\.stopped = true; throw stop; \}/, 'a stop files nothing');
|
|
@@ -181,16 +181,18 @@ test('the day room is the mockup\'s, drawn from the fifteen, and its motion sits
|
|
|
181
181
|
assert.doesNotMatch(STYLE.replace(calm, ''), /rmDrift 60s|animation:rmTwinkle|animation:rmBreathe/, 'no room motion outside the no-preference block');
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
-
test('part 03
|
|
184
|
+
test('part 03 waits for the project, then is one list of checked facts with its count, all on show', () => {
|
|
185
185
|
assert.match(part('panel-done') || PAGE, /id="panel-done"/);
|
|
186
186
|
assert.match(PAGE, /<div class="part waiting" id="panel-done" aria-labelledby="done-part-h">/, 'part 03 waits until the project exists');
|
|
187
|
+
assert.match(PAGE, /<h2 id="done-part-h"><span class="n">03<\/span><span id="donePartT">Connect GitHub<\/span> <span class="state" id="ghState">opens once your project exists<\/span><\/h2>/,
|
|
188
|
+
'before Create it is Connect GitHub; the script names it "Getting <name> ready" after');
|
|
187
189
|
assert.match(SCRIPT, /done\.classList\.toggle\('waiting', !created\);/);
|
|
188
|
-
const
|
|
189
|
-
assert.match(
|
|
190
|
-
assert.match(
|
|
191
|
-
assert.match(
|
|
192
|
-
assert.doesNotMatch(
|
|
193
|
-
assert.doesNotMatch(STYLE, /humanSteps
|
|
190
|
+
const paint = slice('function paintFacts()', 'function repaintFacts()');
|
|
191
|
+
assert.match(paint, /title\.textContent = 'Getting ' \+ projectName\(\) \+ ' ready';/);
|
|
192
|
+
assert.match(paint, /count\.textContent = setupCount\(home, gh\);/);
|
|
193
|
+
assert.match(slice('function setupCount(home, gh)', 'function setupSentence('), /'ready ✓' : done \+ ' of ' \+ req\.length \+ ' done'/);
|
|
194
|
+
assert.doesNotMatch(slice('/* ── part 03 after Create (task 1004683', '/* ── C8: the wizard'), /innerHTML\s*=/, 'no markup built from strings: the facts are nodes');
|
|
195
|
+
assert.doesNotMatch(STYLE, /humanSteps/, 'the old checklist and its sheet are gone');
|
|
194
196
|
});
|
|
195
197
|
|
|
196
198
|
test('the name is typed, so the handle under it is built from nodes, never markup', () => {
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
// tests/wizard_auto_open.mjs — the checklist's "Open my project automatically when it's
|
|
2
|
-
// ready" tick (task 1004548).
|
|
3
|
-
//
|
|
4
|
-
// WHY. The GitHub-App callback page used to hold the founder on its own stepper and open
|
|
5
|
-
// the instance itself. Since task 1004548 it hands them straight back to the checklist,
|
|
6
|
-
// so the auto-open moved there. The rule it must keep: open ONCE, only when sign-in is
|
|
7
|
-
// wired AND the project answers, only after a wait this page actually watched (so a
|
|
8
|
-
// revisit to a long-finished checklist never jumps away), never when the founder
|
|
9
|
-
// unticked it — and prefer a new tab, falling back to this one when the browser blocks
|
|
10
|
-
// an unprompted tab.
|
|
11
|
-
//
|
|
12
|
-
// Executed, not read: the real AUTO_OPEN_KEY…maybeAutoOpen region is sliced out of the
|
|
13
|
-
// page's inline script and run in a node:vm under stubs (the vm-slice idiom of
|
|
14
|
-
// tests/wizard_intent_resume.mjs). The slice boundaries are asserted, so a rename fails
|
|
15
|
-
// loudly instead of testing nothing.
|
|
16
|
-
//
|
|
17
|
-
// Run: node --test tests/wizard_auto_open.mjs
|
|
18
|
-
|
|
19
|
-
import assert from 'node:assert/strict';
|
|
20
|
-
import { test } from 'node:test';
|
|
21
|
-
import fs from 'node:fs';
|
|
22
|
-
import path from 'node:path';
|
|
23
|
-
import vm from 'node:vm';
|
|
24
|
-
import { fileURLToPath } from 'node:url';
|
|
25
|
-
|
|
26
|
-
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
27
|
-
const LANDING = path.join(ROOT, 'modules', 'public-landing', 'public');
|
|
28
|
-
const HUB = fs.readFileSync(path.join(LANDING, 'projects.html'), 'utf8').replace(/\r\n/g, '\n');
|
|
29
|
-
const SCRIPT = HUB.slice(HUB.lastIndexOf('<script>'), HUB.lastIndexOf('</script>'));
|
|
30
|
-
|
|
31
|
-
const START = SCRIPT.indexOf(" var AUTO_OPEN_KEY = 'cb-open-when-ready';");
|
|
32
|
-
const END = SCRIPT.indexOf(' /* oauth-app — the ADR 0133 one-click GitHub-App manifest flow', START);
|
|
33
|
-
const SLICE = SCRIPT.slice(START, END);
|
|
34
|
-
|
|
35
|
-
test('the auto-open region is still findable (a rename must fail loudly)', () => {
|
|
36
|
-
assert.ok(START > 0 && END > START, 'both slice boundaries found');
|
|
37
|
-
assert.match(SLICE, /function maybeAutoOpen\(\)/);
|
|
38
|
-
assert.match(SLICE, /function autoOpenTick\(\)/);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const READY = { manifest: 'done', instStatus: 'active', addrReady: true };
|
|
42
|
-
|
|
43
|
-
function run({ live = READY, armed = true, pref = null, popupBlocked = false, ticks = 1 } = {}) {
|
|
44
|
-
const store = new Map();
|
|
45
|
-
if (pref !== null) store.set('cb-open-when-ready', pref);
|
|
46
|
-
const calls = { opened: [], assigned: [] };
|
|
47
|
-
const sandbox = {
|
|
48
|
-
live: { ...live },
|
|
49
|
-
resolvedDomain: () => 'mercury.example.com',
|
|
50
|
-
localStorage: { getItem: (k) => (store.has(k) ? store.get(k) : null), setItem: (k, v) => store.set(k, v) },
|
|
51
|
-
window: {
|
|
52
|
-
open: (u, t) => { calls.opened.push([u, t]); return popupBlocked ? null : { opener: 'checklist' }; },
|
|
53
|
-
location: { assign: (u) => { calls.assigned.push(u); } },
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
sandbox.globalThis = sandbox;
|
|
57
|
-
vm.createContext(sandbox);
|
|
58
|
-
vm.runInContext(`${SLICE}\nautoOpenArmed = ${armed ? 'true' : 'false'};\nfor (var i = 0; i < ${ticks}; i++) maybeAutoOpen();\nvar tickHtml = autoOpenTick();`, sandbox);
|
|
59
|
-
return { calls, sandbox };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
test('ready + armed + ticked (the default) → opens the project ONCE, in a new tab', () => {
|
|
63
|
-
const { calls } = run({ ticks: 3 });
|
|
64
|
-
assert.deepEqual(calls.opened, [['https://mercury.example.com', '_blank']], 'one new tab, however many renders follow');
|
|
65
|
-
assert.deepEqual(calls.assigned, [], 'this checklist stays put');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('a browser that blocks the unprompted tab sends THIS tab instead — the founder asked', () => {
|
|
69
|
-
const { calls } = run({ popupBlocked: true });
|
|
70
|
-
assert.deepEqual(calls.assigned, ['https://mercury.example.com']);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('nothing opens until sign-in is wired AND the project answers', () => {
|
|
74
|
-
for (const live of [
|
|
75
|
-
{ ...READY, manifest: 'code_received' },
|
|
76
|
-
{ ...READY, instStatus: 'provisioning' },
|
|
77
|
-
{ ...READY, addrReady: false },
|
|
78
|
-
]) {
|
|
79
|
-
const { calls } = run({ live });
|
|
80
|
-
assert.deepEqual([calls.opened, calls.assigned], [[], []], JSON.stringify(live));
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test('unticked → never opens, and the tick renders unticked', () => {
|
|
85
|
-
const { calls, sandbox } = run({ pref: '0' });
|
|
86
|
-
assert.deepEqual([calls.opened, calls.assigned], [[], []]);
|
|
87
|
-
assert.doesNotMatch(sandbox.tickHtml, / checked/);
|
|
88
|
-
assert.match(run().sandbox.tickHtml, / checked>/, 'the default is ticked');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('a checklist that never watched the wait (a revisit) does not jump away', () => {
|
|
92
|
-
const { calls } = run({ armed: false });
|
|
93
|
-
assert.deepEqual([calls.opened, calls.assigned], [[], []]);
|
|
94
|
-
});
|