feat(shell): Routen, Kopfzeile, Hell/Dunkel, Spenden-Banner
- Router: Routen home, gestalten, cards, community/<id>, reset; Umleitung /?karte=<id> nach /gestalten?karte=<id>; Titel je Route (titleFor); Hash-Ziele (/#community) scrollen den Anker ins Bild, nur-Hash-Wechsel bauen die Seite nicht neu auf. - Kopfzeile: Navigation, Suche (Enter -> /?q=...#community), Spenden-Knopf, Hell/Dunkel-Schalter, Menue unter 900 px, Konto als Avatar-Kreis mit Menue. - Theme: data-theme auf <html> (Skript im <head> gegen Aufblitzen), helle Tokens, danger-/warn-/ok-text-Tokens statt fester Farben. - Spenden-Banner (schliessbar); ohne DONATE_URL bleiben Banner und Knopf weg. - User.labels und isAdmin; Platzhalter fuer Startseite und Detailseite. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1a64e12bc5
commit
25ff496548
30 files changed
+1783
-189
No files matched your search
@@ -29,7 +29,7 @@ describe('getCurrentUser', () => {
|
||||
it('liefert den Nutzer und merkt sich die ID', async () => {
|
||||
const auth = await freshAuth();
|
||||
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de', name: 'a' });
|
||||
await expect(auth.getCurrentUser()).resolves.toEqual({ $id: 'u1', email: 'a@b.de' });
|
||||
await expect(auth.getCurrentUser()).resolves.toEqual({ $id: 'u1', email: 'a@b.de', labels: [] });
|
||||
expect(auth.currentUserId()).toBe('u1');
|
||||
});
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('getCurrentUser', () => {
|
||||
await auth.getCurrentUser();
|
||||
await auth.getCurrentUser();
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toHaveBeenLastCalledWith({ $id: 'u1', email: 'a@b.de' });
|
||||
expect(listener).toHaveBeenLastCalledWith({ $id: 'u1', email: 'a@b.de', labels: [] });
|
||||
mocks.get.mockRejectedValue(fail(401, 'general_unauthorized_scope'));
|
||||
await auth.getCurrentUser();
|
||||
await auth.getCurrentUser();
|
||||
@@ -80,6 +80,52 @@ describe('getCurrentUser', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Labels und isAdmin', () => {
|
||||
it('getCurrentUser übernimmt die Labels; fehlen sie (oder sind sie kein Array aus Texten), sind es []', async () => {
|
||||
const auth = await freshAuth();
|
||||
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: ['admin', 'x'] });
|
||||
await expect(auth.getCurrentUser()).resolves.toEqual({ $id: 'u1', email: 'a@b.de', labels: ['admin', 'x'] });
|
||||
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de' });
|
||||
await expect(auth.getCurrentUser()).resolves.toMatchObject({ labels: [] });
|
||||
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: 'admin' });
|
||||
await expect(auth.getCurrentUser()).resolves.toMatchObject({ labels: [] });
|
||||
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: ['admin', 7, null] });
|
||||
await expect(auth.getCurrentUser()).resolves.toMatchObject({ labels: ['admin'] });
|
||||
});
|
||||
|
||||
it('login liefert die Labels ebenfalls', async () => {
|
||||
const auth = await freshAuth();
|
||||
mocks.createEmailPasswordSession.mockResolvedValue({});
|
||||
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de', labels: ['admin'] });
|
||||
await expect(auth.login('a@b.de', 'geheim123')).resolves.toMatchObject({ labels: ['admin'] });
|
||||
expect(auth.currentUser()?.labels).toEqual(['admin']);
|
||||
});
|
||||
|
||||
it('eine Änderung nur der Labels benachrichtigt die Abonnenten', async () => {
|
||||
const auth = await freshAuth();
|
||||
const listener = vi.fn();
|
||||
auth.onAuthChange(listener);
|
||||
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: [] });
|
||||
await auth.getCurrentUser();
|
||||
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: [] });
|
||||
await auth.getCurrentUser();
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: ['admin'] });
|
||||
await auth.getCurrentUser();
|
||||
expect(listener).toHaveBeenCalledTimes(2);
|
||||
expect(listener).toHaveBeenLastCalledWith({ $id: 'u1', email: 'a@b.de', labels: ['admin'] });
|
||||
});
|
||||
|
||||
it('isAdmin ist nur mit dem Label „admin“ wahr', async () => {
|
||||
const auth = await freshAuth();
|
||||
expect(auth.isAdmin(null)).toBe(false);
|
||||
expect(auth.isAdmin({ $id: 'u1', email: 'a@b.de' })).toBe(false);
|
||||
expect(auth.isAdmin({ $id: 'u1', email: 'a@b.de', labels: [] })).toBe(false);
|
||||
expect(auth.isAdmin({ $id: 'u1', email: 'a@b.de', labels: ['administrator', 'Admin'] })).toBe(false);
|
||||
expect(auth.isAdmin({ $id: 'u1', email: 'a@b.de', labels: ['x', 'admin'] })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('login', () => {
|
||||
it('legt die Sitzung an, holt den Nutzer und benachrichtigt', async () => {
|
||||
const auth = await freshAuth();
|
||||
@@ -87,10 +133,10 @@ describe('login', () => {
|
||||
auth.onAuthChange(listener);
|
||||
mocks.createEmailPasswordSession.mockResolvedValue({});
|
||||
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
||||
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de' });
|
||||
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de', labels: [] });
|
||||
expect(mocks.createEmailPasswordSession).toHaveBeenCalledWith({ email: 'a@b.de', password: 'geheim123' });
|
||||
expect(auth.currentUserId()).toBe('u1');
|
||||
expect(listener).toHaveBeenCalledWith({ $id: 'u1', email: 'a@b.de' });
|
||||
expect(listener).toHaveBeenCalledWith({ $id: 'u1', email: 'a@b.de', labels: [] });
|
||||
});
|
||||
|
||||
it('bei falschem Passwort fliegt der Fehler weiter und nichts ändert sich', async () => {
|
||||
@@ -114,7 +160,7 @@ describe('login bei bereits aktiver Sitzung (user_session_already_exists)', () =
|
||||
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockResolvedValueOnce({});
|
||||
mocks.deleteSession.mockResolvedValue({});
|
||||
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
||||
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de' });
|
||||
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de', labels: [] });
|
||||
expect(mocks.deleteSession).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.deleteSession).toHaveBeenCalledWith({ sessionId: 'current' });
|
||||
expect(mocks.createEmailPasswordSession).toHaveBeenCalledTimes(2);
|
||||
@@ -127,7 +173,7 @@ describe('login bei bereits aktiver Sitzung (user_session_already_exists)', () =
|
||||
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockResolvedValueOnce({});
|
||||
mocks.deleteSession.mockRejectedValue(fail(401, 'general_unauthorized_scope'));
|
||||
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
||||
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de' });
|
||||
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de', labels: [] });
|
||||
expect(mocks.createEmailPasswordSession).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
@@ -177,7 +223,7 @@ describe('login bei bereits aktiver Sitzung (user_session_already_exists)', () =
|
||||
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockResolvedValueOnce({});
|
||||
mocks.deleteSession.mockResolvedValue({});
|
||||
mocks.get.mockResolvedValue({ $id: 'u2', email: 'neu@b.de' });
|
||||
await expect(auth.register('neu@b.de', 'geheim123')).resolves.toEqual({ $id: 'u2', email: 'neu@b.de' });
|
||||
await expect(auth.register('neu@b.de', 'geheim123')).resolves.toEqual({ $id: 'u2', email: 'neu@b.de', labels: [] });
|
||||
expect(mocks.deleteSession).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -188,7 +234,7 @@ describe('register', () => {
|
||||
mocks.create.mockResolvedValue({});
|
||||
mocks.createEmailPasswordSession.mockResolvedValue({});
|
||||
mocks.get.mockResolvedValue({ $id: 'u2', email: 'neu@b.de' });
|
||||
await expect(auth.register('neu@b.de', 'geheim123')).resolves.toEqual({ $id: 'u2', email: 'neu@b.de' });
|
||||
await expect(auth.register('neu@b.de', 'geheim123')).resolves.toEqual({ $id: 'u2', email: 'neu@b.de', labels: [] });
|
||||
const params = mocks.create.mock.calls[0][0];
|
||||
expect(params).toMatchObject({ email: 'neu@b.de', password: 'geheim123', name: 'neu' });
|
||||
expect(typeof params.userId).toBe('string');
|
||||
|
||||
Reference in new issue
Block a user