source: Klonkt/test/messages-wave.test.js

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

Zwaaien is een klik, geen paginawissel

Een zwaai is een seintje. Dat de hele pagina ervoor herlaadde -- met een
groene balk bovenaan en je scrollpositie kwijt -- gaf het meer gewicht dan
het heeft.

Nu onderschept mod/messages.js de verzending en stuurt hem met fetch, zoals
like/boost in de Krant al deden. De knoppen maken plaats voor een korte
bevestiging; mislukt het, dan komen ze terug met een schud.

DE VAL HIER, en hij verschilt van like/boost: deze formulieren hebben MEERDERE
verzendknoppen ("Wat leuk!", "Hoe gaat het?", "Terug"), elk met een eigen
name="text". FormData(f) neemt de aangeklikte knop NIET mee -- dat doet de
browser alleen bij een echte verzending. Zonder e.submitter vertrekt er dus
een zwaai zonder tekst. Er is een test die precies dat vastlegt: een verzoek
zonder text moet 400 geven en NIETS versturen.

Het formulier blijft een formulier: zonder JS post het en leidt de route om,
ongewijzigd. De route kiest op X-Requested-With welke van de twee.

Geen aan/uit-knop zoals bij een like: zwaaien valt niet terug te draaien, dus
een toggle zou liegen over wat er kan.

Eigen CSS voor de twee toestanden, want de bestaande klasse nudge bleek
alleen op authorize-interaction opmaak te hebben -- op Berichten deed hij niets
(en doet dat nog steeds niet voor de bookmarklet-knop daar).

5 tests erbij.

Co-Authored-By: Claude Opus 5 <noreply@…>

  • Property mode set to 100644
File size: 4.4 KB
Line 
1// Zwaaien ter plekke: de route moet twee soorten bezoekers bedienen.
2//
3// Met X-Requested-With: fetch antwoordt hij JSON en blijft de pagina staan;
4// zonder dat blijft het een gewoon formulier dat post en omleidt. Dat tweede
5// pad is geen restant maar de no-JS-weg, en het moet werken blijven.
6import { test } from 'node:test';
7import assert from 'node:assert/strict';
8
9process.env.DATABASE_PATH = ':memory:';
10process.env.PUBLIC_BASE_URL = 'https://test.example';
11
12const dbMod = await import('../src/config/database.js');
13const db = dbMod.default;
14dbMod.initializeDatabase();
15
16// De aflevering onderscheppen: deze test gaat over het HTTP-gedrag van de
17// route, niet over federatie. Wat er de deur uit gaat leggen we wel vast.
18const APmod = await import('../src/services/ActivityPubService.js');
19const AP = APmod.default;
20let bezorgd = [];
21let bezorgingLukt = true;
22AP.deliverDirectNote = async (site, opts) => { bezorgd.push(opts); return bezorgingLukt; };
23
24const express = (await import('express')).default;
25const session = (await import('express-session')).default;
26const routes = (await import('../src/routes/posts.js')).default;
27
28db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
29 .run('u1', 'u1', 'u1@t', 'x', 'god');
30db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_public, is_primary) VALUES (?,?,?,?,1,1)')
31 .run('s1', 'band', 'De Band', 'u1');
32
33const app = express();
34app.use(session({ secret: 't', resave: false, saveUninitialized: false }));
35app.use((req, res, next) => {
36 req.session.userId = 'u1';
37 req.session.user = { id: 'u1', username: 'u1', role: 'god' };
38 res.locals.site = db.prepare("SELECT * FROM sites WHERE id = 's1'").get();
39 res.locals.siteUrlBase = '';
40 res.locals.user = req.session.user;
41 next();
42});
43app.use(routes);
44const server = app.listen(0);
45await new Promise((r) => server.once('listening', r));
46const base = `http://127.0.0.1:${server.address().port}`;
47
48const zwaai = (velden, headers = {}) => fetch(base + '/messages/quick-reply', {
49 method: 'POST', redirect: 'manual',
50 headers: { 'Content-Type': 'application/x-www-form-urlencoded', ...headers },
51 body: new URLSearchParams(velden),
52});
53
54test('met de fetch-header: JSON terug, geen omleiding', async () => {
55 bezorgd = []; bezorgingLukt = true;
56 const r = await zwaai({ to: 'https://elders.example/users/kim', text: '👋' }, { 'X-Requested-With': 'fetch' });
57 assert.equal(r.status, 200);
58 assert.equal((r.headers.get('content-type') || '').split(';')[0], 'application/json');
59 assert.deepEqual(await r.json(), { ok: true });
60 assert.equal(bezorgd.length, 1);
61 assert.equal(bezorgd[0].text, '👋');
62 assert.equal(bezorgd[0].wave, true, 'het blijft een zwaai, geen gewoon bericht');
63});
64
65test('zonder die header blijft het de oude weg: omleiden met wave_sent', async () => {
66 bezorgd = []; bezorgingLukt = true;
67 const r = await zwaai({ to: 'https://elders.example/users/kim', text: 'Wat leuk!' });
68 assert.equal(r.status, 302);
69 assert.match(r.headers.get('location'), /\/messages\?success=wave_sent$/);
70 assert.equal(bezorgd[0].text, 'Wat leuk!', 'de gekozen tekst, niet een vaste');
71});
72
73test('een zwaai zonder tekst wordt geweigerd -- de val bij meerdere verzendknoppen', async () => {
74 // FormData neemt de aangeklikte knop niet mee; vergeet de module de submitter,
75 // dan komt het verzoek hier ZONDER text aan. Dan hoort het te falen, niet een
76 // leeg bericht te versturen.
77 bezorgd = [];
78 const r = await zwaai({ to: 'https://elders.example/users/kim' }, { 'X-Requested-With': 'fetch' });
79 assert.equal(r.status, 400);
80 assert.equal((await r.json()).ok, false);
81 assert.equal(bezorgd.length, 0, 'er mag niets vertrokken zijn');
82});
83
84test('een onbruikbare ontvanger geeft 400 in JSON, en 302 zonder de header', async () => {
85 bezorgd = [];
86 const a = await zwaai({ to: 'geen-uri', text: '👋' }, { 'X-Requested-With': 'fetch' });
87 assert.equal(a.status, 400);
88 const b = await zwaai({ to: 'geen-uri', text: '👋' });
89 assert.equal(b.status, 302);
90 assert.match(b.headers.get('location'), /error=quickreply/);
91 assert.equal(bezorgd.length, 0);
92});
93
94test('mislukte aflevering: geen valse bevestiging', async () => {
95 bezorgd = []; bezorgingLukt = false;
96 const r = await zwaai({ to: 'https://elders.example/users/kim', text: '👋' }, { 'X-Requested-With': 'fetch' });
97 assert.equal(r.status, 400);
98 assert.equal((await r.json()).ok, false);
99});
100
101test.after(() => server.close());
Note: See TracBrowser for help on using the repository browser.