Refactor API and enhance Angular integration
- Removed `ciTargetName` from `nx.json`. - Updated `package.json` to include new dependencies: `@types/seedrandom`, `fast-check`, `happy-dom`, and `@nestjs/schedule`. - Modified `pnpm-lock.yaml` to reflect the addition of new packages and their versions. - Improved project documentation in `PROJECT_CONTEXT.md` to clarify the use of Zod schemas and Angular framework decisions. - Introduced new Angular components and patterns in the `.agents/skills/frontend-angular` directory, including examples and reference materials for Angular 21+ features.
This commit is contained in:
107
apps/overlay/src/app/twitch/twitch-auth.service.spec.ts
Normal file
107
apps/overlay/src/app/twitch/twitch-auth.service.spec.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { TwitchAuthService, TwitchJwtPayload } from './twitch-auth.service';
|
||||
|
||||
function makeJwt(payload: Partial<TwitchJwtPayload> = {}): string {
|
||||
const full: TwitchJwtPayload = {
|
||||
exp: Math.floor(Date.now() / 1000) + 3600,
|
||||
opaque_user_id: 'U12345678',
|
||||
user_id: '12345678',
|
||||
channel_id: '987654321',
|
||||
role: 'viewer',
|
||||
...payload,
|
||||
};
|
||||
const encoded = btoa(JSON.stringify(full))
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=/g, '');
|
||||
return `eyJhbGciOiJIUzI1NiJ9.${encoded}.sig`;
|
||||
}
|
||||
|
||||
function makeTwitchAuth(payload: Partial<TwitchJwtPayload> = {}): TwitchAuth {
|
||||
return {
|
||||
channelId: payload.channel_id ?? '987654321',
|
||||
clientId: 'test_client',
|
||||
token: makeJwt(payload),
|
||||
userId: payload.opaque_user_id ?? 'U12345678',
|
||||
};
|
||||
}
|
||||
|
||||
describe('TwitchAuthService', () => {
|
||||
let service: TwitchAuthService;
|
||||
let extCallbacks: {
|
||||
onAuthorized?: (auth: TwitchAuth) => void;
|
||||
onContext?: (ctx: TwitchContext, changed: (keyof TwitchContext)[]) => void;
|
||||
onVisibilityChanged?: (visible: boolean, ctx: TwitchContext) => void;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
extCallbacks = {};
|
||||
(window as Window & { Twitch?: unknown }).Twitch = {
|
||||
ext: {
|
||||
onAuthorized: (cb: (auth: TwitchAuth) => void) => { extCallbacks.onAuthorized = cb; },
|
||||
onContext: (cb: (ctx: TwitchContext, changed: (keyof TwitchContext)[]) => void) => { extCallbacks.onContext = cb; },
|
||||
onVisibilityChanged: (cb: (visible: boolean, ctx: TwitchContext) => void) => { extCallbacks.onVisibilityChanged = cb; },
|
||||
listen: () => undefined,
|
||||
unlisten: () => undefined,
|
||||
send: () => undefined,
|
||||
},
|
||||
};
|
||||
|
||||
service = new TwitchAuthService();
|
||||
service.init();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete (window as Window & { Twitch?: unknown }).Twitch;
|
||||
});
|
||||
|
||||
it('exposes null auth before onAuthorized fires', () => {
|
||||
expect(service.auth()).toBeNull();
|
||||
expect(service.jwtPayload()).toBeNull();
|
||||
expect(service.isLoggedIn()).toBe(false);
|
||||
expect(service.channelId()).toBeNull();
|
||||
});
|
||||
|
||||
it('sets auth and decoded payload when onAuthorized fires', () => {
|
||||
extCallbacks.onAuthorized!(makeTwitchAuth());
|
||||
|
||||
expect(service.auth()).not.toBeNull();
|
||||
expect(service.jwtPayload()?.opaque_user_id).toBe('U12345678');
|
||||
expect(service.jwtPayload()?.channel_id).toBe('987654321');
|
||||
expect(service.channelId()).toBe('987654321');
|
||||
});
|
||||
|
||||
it('reports isLoggedIn true for U-prefixed opaque_user_id', () => {
|
||||
extCallbacks.onAuthorized!(makeTwitchAuth({ opaque_user_id: 'U99999999' }));
|
||||
expect(service.isLoggedIn()).toBe(true);
|
||||
});
|
||||
|
||||
it('reports isLoggedIn false for A-prefixed opaque_user_id', () => {
|
||||
extCallbacks.onAuthorized!(makeTwitchAuth({ opaque_user_id: 'Aanonymous' }));
|
||||
expect(service.isLoggedIn()).toBe(false);
|
||||
});
|
||||
|
||||
it('defaults isVisible to true', () => {
|
||||
expect(service.isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
it('updates isVisible when onVisibilityChanged fires', () => {
|
||||
const ctx = {} as TwitchContext;
|
||||
extCallbacks.onVisibilityChanged!(false, ctx);
|
||||
expect(service.isVisible()).toBe(false);
|
||||
|
||||
extCallbacks.onVisibilityChanged!(true, ctx);
|
||||
expect(service.isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
it('updates context when onContext fires', () => {
|
||||
const ctx = { game: 'test-game' } as TwitchContext;
|
||||
extCallbacks.onContext!(ctx, ['game']);
|
||||
expect(service.context()?.game).toBe('test-game');
|
||||
});
|
||||
|
||||
it('updates context from onVisibilityChanged', () => {
|
||||
const ctx = { isFullScreen: true } as TwitchContext;
|
||||
extCallbacks.onVisibilityChanged!(false, ctx);
|
||||
expect(service.context()?.isFullScreen).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user