feat: replace generated nicknames with filtered user input

This commit is contained in:
2026-10-26 18:04:09 +09:00
parent 5cef1c6f1e
commit fc45534c7d
11 changed files with 147 additions and 40 deletions
+35 -1
View File
@@ -26,7 +26,9 @@ function documentSurface() {
return this.nodes.get(selector)!;
}
addEventListener(name: string, listener: (event: any) => void) { this.listeners.set(name, listener); }
prepend() {} after() {} append() {} showModal() {} close() {}
open = false;
prepend() {} after() {} append() {} setAttribute() {} focus() {}
showModal() { this.open = true; } close() { this.open = false; }
}
const document = {
body: new Element(), createElement: () => new Element(), querySelector: () => new Element(),
@@ -38,6 +40,38 @@ function documentSurface() {
return document;
}
test('first visit waits for a saved nickname and keeps the form open on rejection', async t => {
const document = documentSurface(), original = Object.getOwnPropertyDescriptor(globalThis, 'document');
Object.defineProperty(globalThis, 'document', { configurable: true, value: document });
t.after(() => { if (original) Object.defineProperty(globalThis, 'document', original); else Reflect.deleteProperty(globalThis, 'document'); });
const profile: PlayerProfile = { playerId: 'a', displayName: '', appearance: normalizeAppearance(undefined),
stats: { wins: 0, losses: 0, draws: 0, current: 0, best: 0 }, unlockedColors: ['white'], unlockedEquipment: [] };
let writes = 0, initialized = false, rejectSave = true;
const api = { async request(_path: string, method?: string, body?: { displayName: string }) {
if (method === 'POST') {
writes++;
if (rejectSave) throw new Error('저장 실패');
return { ...profile, displayName: body!.displayName };
}
return profile;
} } as unknown as GameApi;
const lobby = createLobbyFeatures({ api, onQueue() {}, onMatch() {}, onHome() {}, onAppearance() {}, onAvailabilityChange() {}, onError(error) { throw error; } });
const loading = lobby.initialize(false).then(() => { initialized = true; });
await Promise.resolve();
const dialog = document.getElementById('nickname-dialog');
assert.equal(dialog.open, true); assert.equal(initialized, false);
let prevented = false;
dialog.listeners.get('cancel')!({ preventDefault() { prevented = true; } });
assert.equal(prevented, true);
const submit = () => dialog.querySelector('form').listeners.get('submit')!({ preventDefault() {} });
dialog.querySelector('input').value = '씨 발'; await submit();
assert.equal(writes, 0); assert.equal(dialog.open, true);
dialog.querySelector('input').value = '손오공'; await submit();
assert.equal(initialized, false); assert.equal(dialog.querySelector('p').textContent, '저장 실패');
rejectSave = false; await submit(); await loading;
assert.equal(initialized, true); assert.equal(dialog.open, false);
});
test('ranked confirmation shows accept, waits for opponent and cancels by offer id', async t => {
const document = documentSurface(), original = Object.getOwnPropertyDescriptor(globalThis, 'document');
Object.defineProperty(globalThis, 'document', { configurable: true, value: document });