source: Klonkt/test/migrate-page.test.js

main
Last change on this file was 78b8e58, checked in by Robin <roboburr@…>, 2 weeks ago

Toetsen: layout-pins eruit, overlap samengevoegd (mixtape, migrate, c2s-titel)

Dezelfde maat als bij de Berichten-toetsen (Robins besluit, 25-8): een
toets pint een beslissing, niet waar iets staat of hoe de code gespeld
is. Drie bestanden, netto drie toetsen minder en vier pins weg:

  • mixtape-ui: de bron-regex-toets op de labelmap weg (die toetste dezelfde regel als de render-toets, maar dan op de spelling van de code); de klasse-pin rond het woord Mixtape los (dat het er staat is het besluit, waar het staat is layout); de spoel-asserts weg -- de cassette-decoratie is layout, de haken van tape.js (.post-tape, data-tape-go) blijven, want dat is het contract met de module.
  • migrate-page: de twee volgorde-toetsen samengevoegd. De volgorde ZELF blijft getoetst, want daar is hij inhoud: wie de stappen verkeerd om doet breekt zijn verhuizing. De positie-assert van het stappenplan (bovenaan) is weg, het bestaan ervan blijft.
  • c2s-title: de twee lege-titel-gevallen (geen name, alleen witruimte) zijn een regel met twee kanten, nu een toets.

Bij het echt lezen bleken twee eerder verdachte toetsen juist
beslissingstoetsen: 'de teksten praten geen jargon' bewaakt Robins eigen
regel en toetst op betekenis-niveau, en 'stap 5' is daar destijds al
bewust op herschreven. Die blijven onaangeroerd, net als de tien
speler-toetsen die broncode lezen -- brosse dekking, maar de enige die
browser-JS hier heeft; schrappen kan pas als er iets voor in de plaats
komt.

Tegenbewijs per snede: het mixtape-label valt tegen de renderer van voor
8e1af9c, de volgorde valt tegen een omgedraaide pagina, en de lege titel
valt tegen een mutatie die er een verzint. Volle suite 1229 groen.

  • Property mode set to 100644
File size: 8.4 KB
Line 
1// Alle migratie-opties op één pagina (Robin, 14-8), en wat daarbij stuk kon.
2//
3// Aliassen en verhuizen stonden op de site-bewerkpagina, tussen de kleuren en
4// de feedinstellingen. Dat is de verkeerde plek: het zijn stap 1 en stap 4 van
5// een verhuizing, en het scherm eromheen gaat over hoe je site eruitziet.
6//
7// De verplaatsing had een stille valkuil: de opslagroute van dat scherm schreef
8// ap_aliases uit het formulier. Verdwijnt het veld, dan is de waarde leeg, en
9// dan wist je je claim op je oude account door je accentkleur te wijzigen. De
10// Move weigert daarna met no_backreference en je snapt niet waarom.
11import { test } from 'node:test';
12import assert from 'node:assert/strict';
13import fs from 'fs';
14const i18nMod = await import('../src/services/i18n.js');
15
16process.env.DATABASE_PATH = ':memory:';
17process.env.PUBLIC_BASE_URL = 'https://ik.example';
18
19const dbMod = await import('../src/config/database.js');
20const db = dbMod.default;
21{ const stil = console.log; console.log = () => {}; try { dbMod.initializeDatabase(); } finally { console.log = stil; } }
22
23test('het site-bewerkscherm draagt geen alias- of verhuisformulier meer', () => {
24 const ejs = fs.readFileSync('src/views/pages/admin-site-edit.ejs', 'utf8');
25 assert.ok(!/name="ap_aliases"/.test(ejs), 'het aliasveld hoort bij Migreren');
26 assert.ok(!/name="move_target"/.test(ejs), 'en de verhuisknop ook');
27 assert.match(ejs, /\/admin\/migrate/, 'maar er staat wel een wegwijzer, anders zoekt iemand zich rot');
28});
29
30test('de migrate-pagina draagt ze allebei wel', () => {
31 const ejs = fs.readFileSync('src/views/pages/admin-migrate.ejs', 'utf8');
32 assert.match(ejs, /name="ap_aliases"/);
33 assert.match(ejs, /name="move_target"/);
34 // De verhuisknop post naar de BESTAANDE route: een tweede implementatie van
35 // een onomkeerbare actie is precies wat je niet wilt.
36 assert.match(ejs, /action="\/admin\/sites\/<%= site\.slug %>\/move"/);
37 assert.match(ejs, /name="next" value="\/admin\/migrate"/, 'en komt terug waar je vandaan kwam');
38});
39
40test('de volgorde op de pagina volgt de verhuizing: claimen eerst, aankondigen laatst', () => {
41 // Volgorde is hier INHOUD, geen layout (25-8): wie deze stappen in de
42 // verkeerde volgorde doet, breekt zijn verhuizing -- de pagina IS het
43 // stappenplan. Daarom mag deze positietoets blijven waar andere sneuvelden.
44 // De losse toets dat het stappenplan "bovenaan, voor de knoppen" staat is
45 // hierin opgegaan: dat het er IS hoort erbij, waar het staat niet.
46 const ejs = fs.readFileSync('src/views/pages/admin-migrate.ejs', 'utf8');
47 assert.ok(ejs.indexOf('mig-steps') > -1, 'er hoort een stappenplan te staan');
48 const alias = ejs.indexOf('name="ap_aliases"');
49 const halen = ejs.indexOf('/admin/migrate/pull');
50 const move = ejs.indexOf('name="move_target"');
51 assert.ok(alias > -1 && halen > -1 && move > -1);
52 assert.ok(alias < halen, 'claimen staat boven ophalen: zonder claim geeft de bron niets');
53 assert.ok(halen < move, 'aankondigen staat onderaan: dat is de onomkeerbare stap');
54});
55
56test('een alias overleeft het opslaan van je uiterlijk', async () => {
57 // De valkuil. Geen HTTP nodig: dit gaat om de regel dat een ontbrekend veld
58 // "niet aanraken" betekent en niet "leegmaken".
59 db.prepare("INSERT INTO users (id,username,email,password_hash,role) VALUES ('u1','u','u@t','x','god')").run();
60 db.prepare("INSERT INTO sites (id,slug,title,owner_id,ap_aliases) VALUES ('s1','ik','Ik','u1',?)")
61 .run(JSON.stringify(['https://oud.example/ap/users/robo']));
62
63 const site = db.prepare("SELECT id, ap_aliases FROM sites WHERE slug = 'ik'").get();
64 const formulierZonderVeld = { title: 'Ik', accent: '#c33' }; // zoals het nu binnenkomt
65 const apAliasesJson = Object.prototype.hasOwnProperty.call(formulierZonderVeld, 'ap_aliases')
66 ? null : (site.ap_aliases || null);
67 db.prepare('UPDATE sites SET ap_aliases = ? WHERE slug = ?').run(apAliasesJson, 'ik');
68
69 const na = db.prepare("SELECT ap_aliases FROM sites WHERE slug = 'ik'").get();
70 assert.deepEqual(JSON.parse(na.ap_aliases), ['https://oud.example/ap/users/robo'],
71 'je claim op je oude account mag niet sneuvelen op een kleurwijziging');
72});
73
74// ── Het stappenplan ───────────────────────────────────────────────
75
76test('"hier" draait om als je vanaf de vertrekkende kant kijkt', async () => {
77 // Deze pagina draait op BEIDE instanties. Stap 1, 3 en 4 horen op de nieuwe,
78 // stap 2 en 5 op de oude. Een vaste tekst is dus op de helft van de schermen
79 // onzin. Ik had ze bij het bouwen precies verkeerd om: beide labels werden
80 // "hier", en dat zag ik pas door het te renderen.
81 const ejs = fs.readFileSync('src/views/pages/admin-migrate.ejs', 'utf8');
82 const m = /var _hier = stap\.isOud \? t\('([a-z.\_]+)'\)\s*:\s*t\('([a-z.\_]+)'\)/.exec(ejs);
83 const d = /var _daar = stap\.isOud \? t\('([a-z.\_]+)'\)\s*:\s*t\('([a-z.\_]+)'\)/.exec(ejs);
84 assert.ok(m && d, 'de perspectief-variabelen horen er te zijn');
85 assert.equal(m[1], 'mig.plan_new', 'op de oude kant wijst stap 1/3/4 naar de NIEUWE');
86 assert.equal(m[2], 'mig.plan_here', 'op de nieuwe kant is dat gewoon hier');
87 assert.equal(d[1], 'mig.plan_here', 'en het aankondigen gebeurt dan HIER');
88 assert.equal(d[2], 'mig.plan_old', 'terwijl je vanaf de nieuwe kant naar de oude wijst');
89 assert.notEqual(m[1], d[1], 'de twee mogen nooit hetzelfde label krijgen');
90});
91
92test('het aankondigen heet geen "laatste stap" meer', async () => {
93 // Het was er wel een in mijn hoofd, maar niet in de code: de ophaalknop
94 // weigert met not_moved_here zolang de Move niet gedaan is. Aankondigen is
95 // dus stap 2, niet de afsluiter.
96 const i18n = await import('../src/services/i18n.js');
97 for (const l of ['nl', 'en', 'de']) {
98 const t = i18n.t(l, 'mig.move_title');
99 assert.doesNotMatch(t, /laatste|last step|letzter/i, `${l}: ${t}`);
100 assert.match(t, /2/, `${l}: het is stap 2, en dat hoort er te staan`);
101 }
102});
103
104test('stap 5 vertelt wat er moet gebeuren voor je het domein loslaat', async () => {
105 // Er is geen knop voor, en dat staat er ook bij. Een stappenplan dat een stap
106 // beschrijft die niet bestaat is erger dan geen stappenplan.
107 const i18n = await import('../src/services/i18n.js');
108 // Op BETEKENIS toetsen, niet op een woord. Deze test brak toen "alias" uit de
109 // teksten verdween, en dat is precies de verkeerde reden om rood te worden:
110 // het jargon weghalen was de bedoeling.
111 const nl = i18n.t('nl', 'mig.plan_5_why');
112 assert.match(nl, /verwijder/i, 'de berichten opruimen');
113 assert.match(nl, /stap 1/i, 'en de koppeling met je vorige account weghalen');
114 assert.match(nl, /nog geen knop/i, 'en eerlijk dat het handwerk is');
115});
116
117test('de teksten praten geen jargon', () => {
118 // Robin: er zijn miljoenen Klonkt-gebruikers, dus het moet gebruiksvriendelijk.
119 // Woorden als alias, actor, instantie en fediverse zeggen een ontwikkelaar
120 // alles en een gebruiker niets. Deze test bewaakt dat ze niet terugsluipen.
121 const jargon = /\b(alias(sen)?|claimen|actor|instantie|AP-id|origin|slug|URI)\b/i;
122 const sleutels = [
123 'lead', 'alias_title', 'alias_hint', 'alias_label', 'alias_note', 'alias_btn',
124 'import_hint', 'file_label', 'overwrite_label', 'overwrite_hint', 'r_new_ids',
125 'pull_hint', 'pull_source', 'pull_source_hint', 'move_hint', 'move_warn',
126 'plan_hint', 'plan_1', 'plan_1_why', 'plan_5_why',
127 'e_no_backreference', 'e_not_moved_here', 'e_no_source',
128 ];
129 const fout = [];
130 for (const l of ['nl', 'en', 'de']) {
131 for (const k of sleutels) {
132 const t = i18nMod.t(l, `mig.${k}`);
133 const m = jargon.exec(t);
134 if (m) fout.push(`${l}/${k}: "${m[0]}"`);
135 }
136 }
137 assert.deepEqual(fout, [], `jargon teruggeslopen: ${fout.join(', ')}`);
138});
139
140test('een gekoppeld account komt terug als handle, niet als URL', async () => {
141 // Je typt @jij@mastodon.social en wij slaan de actor-URL op, want daar draait
142 // het protocol op. Zonder deze vertaling terug kreeg je een adres te zien dat
143 // je nooit hebt ingetypt en niet herkent.
144 const AP = await import('../src/services/ActivityPubService.js');
145 assert.equal(AP.deriveHandle('https://oud.example/ap/users/robo'), '@robo@oud.example');
146 // En als er geen fatsoenlijke handle uit te halen valt blijft de URL staan:
147 // een verkeerde handle is erger dan een lelijke URL.
148 const raar = AP.deriveHandle('https://oud.example/');
149 assert.ok(!/^@[^@\s]+@[^@\s]+$/.test(raar) || raar === '@oud.example@oud.example',
150 `onverwachte handle uit een pad zonder naam: ${raar}`);
151});
Note: See TracBrowser for help on using the repository browser.