fix(community): Zeile „von …“ bleibt leer, solange der Name des Hochladers noch gesucht wird
Logo-Kacheln, Logo-Infozeile und Detailseite zeigen keinen Ersatz („von Anonym“, alter Anzeigename) mehr, der gleich auf „von @name“ wechselt: Solange die Abfrage für den Hochlader läuft, ist die Zeile leer und behält ihre Höhe (U+200B vor dem leeren Inhalt in logos.css und community.css); danach steht der Name, ohne Profil oder bei einem Fehler der Ersatz. Schon bekannte Hochlader zeigen ihre Zeile sofort, ein gemeldeter Name beendet das Warten. Die Infozeile lässt dabei Zeile und Trenner weg („<Name> · ♥ <Zahl>“), LogoInfoHost bekommt byline(logo) statt names(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
56cb67be4b
commit
dc6bd3362b
9 files changed
+470
-34
No files matched your search
@@ -385,17 +385,165 @@ describe('Benutzernamen („von @name“)', () => {
|
||||
expect(by('Logo A')).toBe('von @Kenso');
|
||||
});
|
||||
|
||||
it('die Liste wartet nicht auf die Namen: bis sie da sind, steht der Ersatz, danach der Name', async () => {
|
||||
it('die Liste wartet nicht auf die Namen: die Kacheln stehen sofort, ihre Zeile „von …“ folgt mit den Namen', async () => {
|
||||
const lookup = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockReturnValue(lookup.promise);
|
||||
await mount();
|
||||
expect(names()).toEqual(['Kein Logo', 'Logo A', 'Logo B', 'Logo C']);
|
||||
expect(['Logo A', 'Logo B', 'Logo C'].map(by)).toEqual(['von Anonym', 'von Anna', 'von Anonym']);
|
||||
expect(['Logo A', 'Logo B', 'Logo C'].map(by)).toEqual(['', '', '']);
|
||||
lookup.resolve(new Map([['u1', 'Kenso'], ['u2', 'Mia']]));
|
||||
await flush();
|
||||
expect(['Logo A', 'Logo B', 'Logo C'].map(by)).toEqual(['von @Kenso', 'von @Mia', 'von Anonym']);
|
||||
});
|
||||
|
||||
describe('solange der Name seines Hochladers noch gesucht wird, ist die Zeile „von …“ leer (kein Ersatz, der gleich wieder wechselt)', () => {
|
||||
const line = (name: string) => cell(name).querySelector('.logo-tile-by')!;
|
||||
|
||||
it('gefunden: danach „von @name“, im selben Element; leer heißt ohne jeden Knoten (die Zeilenhöhe hält die CSS-Regel für :empty)', async () => {
|
||||
const lookup = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockReturnValue(lookup.promise);
|
||||
await mount();
|
||||
const span = line('Logo A');
|
||||
expect(span.textContent).toBe('');
|
||||
expect(span.childNodes).toHaveLength(0); // sonst griffe :empty nicht
|
||||
lookup.resolve(new Map([['u1', 'Kenso']]));
|
||||
await flush();
|
||||
expect(line('Logo A')).toBe(span);
|
||||
expect(span.textContent).toBe('von @Kenso');
|
||||
});
|
||||
|
||||
it('kein Profil: erst jetzt der Ersatz, der alte Anzeigename (ohne @) bzw. „von Anonym“', async () => {
|
||||
const lookup = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockReturnValue(lookup.promise);
|
||||
await mount();
|
||||
expect(['Logo A', 'Logo B', 'Logo C'].map(by)).toEqual(['', '', '']);
|
||||
lookup.resolve(new Map());
|
||||
await flush();
|
||||
expect(['Logo A', 'Logo B', 'Logo C'].map(by)).toEqual(['von Anonym', 'von Anna', 'von Anonym']);
|
||||
});
|
||||
|
||||
it('die Suche scheitert: erst jetzt der Ersatz, kein Fehlertext', async () => {
|
||||
const lookup = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockReturnValue(lookup.promise);
|
||||
await mount();
|
||||
expect(by('Logo B')).toBe('');
|
||||
lookup.reject(new TypeError('Failed to fetch'));
|
||||
await flush();
|
||||
expect(['Logo A', 'Logo B', 'Logo C'].map(by)).toEqual(['von Anonym', 'von Anna', 'von Anonym']);
|
||||
expect(alertText()).toBe('');
|
||||
expect(infoMessage()).toBe('');
|
||||
});
|
||||
|
||||
it('nur die Hochlader, deren Name noch gesucht wird, sind leer; die anderen Zeilen stehen schon (Mehr laden: Namen und „kein Profil“ von der ersten Seite gelten sofort)', async () => {
|
||||
fx.listLogos.mockResolvedValueOnce({ logos: [owned('a', 'u1'), owned('b', 'u2', 'Anna')], nextCursor: 'b' });
|
||||
fx.listLogos.mockResolvedValueOnce({
|
||||
logos: [owned('c', 'u3'), owned('d', 'u1'), owned('e', 'u2', 'Berta')],
|
||||
nextCursor: null,
|
||||
});
|
||||
fx.usernamesFor.mockResolvedValueOnce(new Map([['u1', 'Kenso']])); // erste Seite: u1 hat ein Profil, u2 keins
|
||||
const second = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockReturnValueOnce(second.promise);
|
||||
await mount();
|
||||
expect(['Logo A', 'Logo B'].map(by)).toEqual(['von @Kenso', 'von Anna']);
|
||||
moreButton().click();
|
||||
await flush();
|
||||
expect(by('Logo D')).toBe('von @Kenso'); // der Name steht schon
|
||||
expect(by('Logo E')).toBe('von Berta'); // „kein Profil“ steht schon fest
|
||||
expect(by('Logo C')).toBe(''); // u3 wird noch gesucht
|
||||
second.resolve(new Map([['u3', 'Zora']]));
|
||||
await flush();
|
||||
expect(by('Logo C')).toBe('von @Zora');
|
||||
expect(by('Logo D')).toBe('von @Kenso');
|
||||
});
|
||||
|
||||
it('Einträge ohne Konto-ID haben nichts zu suchen: ihre Zeile steht sofort', async () => {
|
||||
fx.listLogos.mockResolvedValue({ logos: [owned('a', 'u1'), owned('b', '', 'Anna'), owned('c', '')], nextCursor: null });
|
||||
fx.usernamesFor.mockReturnValue(new Promise(() => {}));
|
||||
await mount();
|
||||
expect(['Logo A', 'Logo B', 'Logo C'].map(by)).toEqual(['', 'von Anna', 'von Anonym']);
|
||||
});
|
||||
|
||||
it('die Infozeile: ohne Zeile „von …“ („<Name> · ♥ <Zahl>“), danach mit dem Namen oder dem Ersatz', async () => {
|
||||
const lookup = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockReturnValue(lookup.promise);
|
||||
await mount();
|
||||
picker.select('a');
|
||||
expect(infoLine()).toBe('Logo A · ♥ 0');
|
||||
picker.select('b');
|
||||
expect(infoLine()).toBe('Logo B · ♥ 0');
|
||||
lookup.resolve(new Map([['u1', 'Kenso']]));
|
||||
await flush();
|
||||
expect(infoLine()).toBe('Logo B · von Anna · ♥ 0'); // u2: kein Profil, der alte Anzeigename
|
||||
picker.select('a');
|
||||
expect(infoLine()).toBe('Logo A · von @Kenso · ♥ 0');
|
||||
});
|
||||
|
||||
it('die Infozeile bei gescheiterter Suche: der Ersatz', async () => {
|
||||
const lookup = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockReturnValue(lookup.promise);
|
||||
await mount();
|
||||
picker.select('b');
|
||||
expect(infoLine()).toBe('Logo B · ♥ 0');
|
||||
lookup.reject(new Error('weg'));
|
||||
await flush();
|
||||
expect(infoLine()).toBe('Logo B · von Anna · ♥ 0');
|
||||
expect(infoMessage()).toBe('');
|
||||
});
|
||||
|
||||
it('ein gemeldeter Name (Umbenennen, Lesen des eigenen Profils) beendet das Warten dieses Kontos sofort, die anderen bleiben leer', async () => {
|
||||
const lookup = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockReturnValue(lookup.promise);
|
||||
await mount();
|
||||
picker.select('a');
|
||||
emitName('u1', 'Kenso');
|
||||
expect(['Logo A', 'Logo B', 'Logo C'].map(by)).toEqual(['von @Kenso', '', '']);
|
||||
expect(infoLine()).toBe('Logo A · von @Kenso · ♥ 0');
|
||||
lookup.resolve(new Map([['u1', 'Kenso']]));
|
||||
await flush();
|
||||
expect(['Logo A', 'Logo B', 'Logo C'].map(by)).toEqual(['von @Kenso', 'von Anna', 'von Anonym']);
|
||||
});
|
||||
|
||||
it('das angeheftete Logo (gewählt, nicht in der Liste): leer, bis sein Hochlader nachgeschlagen ist', async () => {
|
||||
fx.getLogo.mockResolvedValue(owned('x', 'u7'));
|
||||
const lookup = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockResolvedValueOnce(new Map()); // die Liste
|
||||
fx.usernamesFor.mockReturnValueOnce(lookup.promise); // das angeheftete Logo
|
||||
await mount();
|
||||
picker.select('x');
|
||||
await flush();
|
||||
expect(by('Logo X')).toBe('');
|
||||
expect(infoLine()).toBe('Logo X · ♥ 0');
|
||||
lookup.resolve(new Map([['u7', 'Zora']]));
|
||||
await flush();
|
||||
expect(by('Logo X')).toBe('von @Zora');
|
||||
expect(infoLine()).toBe('Logo X · von @Zora · ♥ 0');
|
||||
});
|
||||
|
||||
it('ein frisch hochgeladenes Logo eines noch unbekannten Hochladers: leer, bis sein Name da ist', async () => {
|
||||
const lookup = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockResolvedValueOnce(new Map()); // die Liste
|
||||
fx.usernamesFor.mockReturnValueOnce(lookup.promise); // das neue Logo
|
||||
fx.uploadDialog.mockResolvedValue(logo('n', { name: 'Neues Logo', ownerId: 'u5', displayName: '' }));
|
||||
await mount();
|
||||
uploadButton().click();
|
||||
await flush();
|
||||
expect(by('Neues Logo')).toBe('');
|
||||
lookup.resolve(new Map([['u5', 'Kenso']]));
|
||||
await flush();
|
||||
expect(by('Neues Logo')).toBe('von @Kenso');
|
||||
});
|
||||
|
||||
it('wird die Seite aufgeräumt, bevor die Suche fertig ist, ändert eine spätere Antwort nichts', async () => {
|
||||
const lookup = deferred<Map<string, string>>();
|
||||
fx.usernamesFor.mockReturnValue(lookup.promise);
|
||||
await mount();
|
||||
picker.dispose();
|
||||
lookup.resolve(new Map([['u1', 'Kenso']]));
|
||||
await flush();
|
||||
expect(by('Logo A')).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
it('der Name als Text, nie als HTML', async () => {
|
||||
fx.usernamesFor.mockResolvedValue(new Map([['u1', '<b>fett</b>']]));
|
||||
await mount();
|
||||
@@ -408,7 +556,7 @@ describe('Benutzernamen („von @name“)', () => {
|
||||
fx.usernamesFor.mockReturnValue(lookup.promise);
|
||||
await mount();
|
||||
picker.select('a');
|
||||
expect(infoLine()).toBe('Logo A · von Anonym · ♥ 0');
|
||||
expect(infoLine()).toBe('Logo A · ♥ 0'); // der Name wird noch gesucht: keine Zeile „von …“
|
||||
lookup.resolve(new Map([['u1', 'Kenso']]));
|
||||
await flush();
|
||||
expect(infoLine()).toBe('Logo A · von @Kenso · ♥ 0');
|
||||
@@ -538,9 +686,9 @@ describe('Benutzernamen („von @name“)', () => {
|
||||
expect(nameListeners.size).toBe(0);
|
||||
lookup.resolve(new Map([['u1', 'Kenso']]));
|
||||
await flush();
|
||||
expect(by('Logo A')).toBe('von Anonym');
|
||||
expect(by('Logo A')).toBe(''); // die Antwort kam nach dem Aufräumen: nichts ändert sich
|
||||
emitName('u1', 'Neu');
|
||||
expect(by('Logo A')).toBe('von Anonym');
|
||||
expect(by('Logo A')).toBe('');
|
||||
});
|
||||
|
||||
it('Englisch: „by @name“, „by Anna“, „by Anonymous“', async () => {
|
||||
|
||||
Reference in new issue
Block a user