group code popup
Sonarqube Scanner / Prepare environment (push) Successful in 6s
Sonarqube Scanner / Build and test (push) Successful in 27s
Sonarqube Scanner / SonarQube analysis (push) Failing after 1m14s
Sonarqube Scanner / Deploy to Server (push) Successful in 10s

This commit is contained in:
Florian Zumpe
2026-10-18 17:24:41 +02:00
parent 31768ae907
commit 0e4cdfbd54
18 changed files with 400 additions and 49 deletions
+55 -7
View File
@@ -12,7 +12,7 @@ class Element extends EventTarget {
replaceChildren(...nodes) { this.children = nodes; }
setAttribute(name, value) { this.attributes.set(name, value); }
showModal() { this.open = true; }
close() { this.open = false; }
close() { this.open = false; this.dispatchEvent(new Event('close')); }
scrollTo({ top }) { this.scrollTop = top; this.dispatchEvent(new Event('scroll')); }
}
const tick = async () => { for (let i = 0; i < 4; i += 1) await new Promise(resolve => setTimeout(resolve, 0)); };
@@ -27,6 +27,7 @@ function key(node, value) {
}
async function harness(loading = false) {
const ids = ['group-join-open', 'editor-group-join', 'editor-group-list', 'group-dialog', 'group-close',
'group-share-dialog', 'group-share-route', 'group-share-pictures', 'group-share-start', 'group-share-close',
'group-status', 'group-wheels', 'group-entry', 'group-invitation', 'group-code', 'group-join', 'group-edit', 'group-expiry'];
const nodes = new Map(ids.map(id => [id, new Element()]));
const document = new EventTarget();
@@ -48,7 +49,7 @@ async function harness(loading = false) {
if (options.method === 'GET') value = { groups: [group()] };
else if (options.method === 'DELETE') { controls.active = false; value = { closed: true }; }
else if (url.endsWith('/join')) value = { group: group() };
else value = { group: group(), code: Array(8).fill('🐦'), expiresAt: 1800000000 };
else value = { group: group(), code: Array(5).fill('🐦'), expiresAt: 1800000000 };
return { ok: true, json: async () => value };
}
};
@@ -69,7 +70,7 @@ test('group code wheels support arrows, keyboard wrapping, scrolling and joining
pointer(h.nodes.get('group-join-open'));
assert.equal(h.nodes.get('group-dialog').open, true);
const columns = h.nodes.get('group-wheels').children;
assert.equal(columns.length, 8);
assert.equal(columns.length, 5);
const [up, viewport, down] = columns[0].children;
assert.equal(viewport.attributes.get('role'), 'spinbutton');
key(up, 'Enter');
@@ -86,7 +87,7 @@ test('group code wheels support arrows, keyboard wrapping, scrolling and joining
await tick();
const joined = h.calls.find(call => call.url.endsWith('/join'));
const body = JSON.parse(joined.body);
assert.equal(body.code.length, 8);
assert.equal(body.code.length, 5);
assert.equal(body.code[0], '🌼');
assert.equal(body.creator_id, 'a'.repeat(64));
assert.deepEqual(h.edits, [22]);
@@ -96,16 +97,17 @@ test('group code wheels support arrows, keyboard wrapping, scrolling and joining
assert.equal(h.nodes.get('group-dialog').open, false);
});
test('sharing displays eight motifs and the owner can renew or close access with confirmation', async () => {
test('sharing displays five motifs and the owner can renew or close access with confirmation', async () => {
const h = await harness();
await h.groups.share(7);
assert.equal(JSON.parse(h.calls[0].body).routeId, 7);
assert.equal(h.nodes.get('group-code').children.length, 8);
assert.equal(h.nodes.get('group-code').children.length, 5);
assert.equal(h.nodes.get('group-entry').hidden, true);
assert.match(h.nodes.get('group-expiry').textContent, /Code gilt/);
const shareButton = h.groups.shareButton('share-route');
assert.equal(shareButton.dataset.editorAction, 'share-route');
assert.equal(shareButton.children.length, 2);
assert.equal(shareButton.textContent, '👥');
assert.equal(shareButton.attributes.get('aria-haspopup'), 'dialog');
pointer(h.nodes.get('group-edit')); await tick();
assert.deepEqual(h.edits, [22]);
let actions = h.nodes.get('editor-group-list').children[0].children[1].children;
@@ -186,3 +188,49 @@ test('unsent station changes are listed before network access and discarded only
h.context.document.dispatchEvent(new Event('wegwichtel:point-drafts-changed')); await tick();
assert.equal(list.children.length, 0);
});
test('compact sharing button opens a large gnome preview and requires confirmation before starting', async () => {
const h = await harness();
const decision = h.groups.confirmShare('Blumenweg');
const dialog = h.nodes.get('group-share-dialog');
assert.equal(dialog.open, true);
assert.equal(h.nodes.get('group-share-route').textContent, 'Blumenweg');
const pictures = h.nodes.get('group-share-pictures').children;
assert.equal(pictures.length, 2);
assert.match(pictures[0].src, /🐦/);
assert.match(pictures[1].src, /🪨/);
assert.equal(h.calls.length, 0);
assert.equal(await h.groups.confirmShare('Zweiter Weg'), false);
pointer(h.nodes.get('group-share-close'));
assert.equal(await decision, false);
const accepted = h.groups.confirmShare('Blumenweg');
key(h.nodes.get('group-share-start'), 'Enter');
assert.equal(await accepted, true);
assert.equal(dialog.open, false);
const dismissed = h.groups.confirmShare('Blumenweg');
dialog.close();
assert.equal(await dismissed, false);
});
test('the open invitation refreshes its code automatically and shows a deferred renewal during station editing', async () => {
const h = await harness();
await h.groups.share(7);
const original = h.context.fetch;
h.context.fetch = async (url, options) => {
if (!url.endsWith('/invitation')) return original(url, options);
return { ok: true, json: async () => ({ group: { id: 'group-id', route: { id: 22 }, role: 'owner' },
busy: true, expired: true, expiresAt: 1800000000 }) };
};
await h.groups.refreshInvitation();
assert.match(h.nodes.get('group-status').textContent, /Station bearbeitet/);
h.context.fetch = async () => ({ ok: true, json: async () => ({ group: { id: 'group-id', route: { id: 22 }, role: 'owner' },
busy: false, expired: false, expiresAt: 1800001800, code: Array(5).fill('🪨') }) });
await h.groups.refreshInvitation();
assert.equal(h.nodes.get('group-code').children.length, 5);
assert.match(h.nodes.get('group-code').children[0].src, /🪨/);
assert.equal(h.nodes.get('group-status').textContent, '');
h.nodes.get('group-dialog').close();
h.context.fetch = async () => { throw new Error('must not fetch a closed invitation'); };
await h.groups.refreshInvitation();
});