244 lines
8.2 KiB
TypeScript
244 lines
8.2 KiB
TypeScript
import '@angular/compiler';
|
|
import {
|
|
describe,
|
|
it,
|
|
expect,
|
|
vi
|
|
} from 'vitest';
|
|
import {
|
|
Injector,
|
|
runInInjectionContext,
|
|
signal
|
|
} from '@angular/core';
|
|
import { Router } from '@angular/router';
|
|
import { Store } from '@ngrx/store';
|
|
import { of } from 'rxjs';
|
|
|
|
import { DashboardComponent } from './dashboard.component';
|
|
import { ViewportService } from '../../core/platform';
|
|
import { ServerDirectoryFacade } from '../../domains/server-directory/application/facades/server-directory.facade';
|
|
import { FriendService } from '../../domains/direct-message/application/services/friend.service';
|
|
import { RoomsActions } from '../../store/rooms/rooms.actions';
|
|
import {
|
|
selectSearchResults,
|
|
selectIsSearching,
|
|
selectSavedRooms
|
|
} from '../../store/rooms/rooms.selectors';
|
|
import { selectAllUsers, selectCurrentUser } from '../../store/users/users.selectors';
|
|
import type { ServerInfo } from '../../domains/server-directory/domain/models/server-directory.model';
|
|
import type { Room, User } from '../../shared-kernel';
|
|
|
|
interface HarnessOptions {
|
|
searchResults?: ServerInfo[];
|
|
saved?: Room[];
|
|
users?: User[];
|
|
currentUser?: User | null;
|
|
featured?: ServerInfo[];
|
|
trending?: ServerInfo[];
|
|
friendIds?: string[];
|
|
isMobile?: boolean;
|
|
}
|
|
|
|
function makeServer(id: string, name = id): ServerInfo {
|
|
return { id, name, maxUsers: 50, userCount: 1, isPrivate: false } as unknown as ServerInfo;
|
|
}
|
|
|
|
function createHarness(options: HarnessOptions = {}) {
|
|
const dispatch = vi.fn();
|
|
const searchResultsSig = signal<ServerInfo[]>(options.searchResults ?? []);
|
|
const savedSig = signal<Room[]>(options.saved ?? []);
|
|
const usersSig = signal<User[]>(options.users ?? []);
|
|
const currentUserSig = signal<User | null>(options.currentUser ?? null);
|
|
const store = {
|
|
selectSignal: (selector: unknown) => {
|
|
if (selector === selectSearchResults) {
|
|
return searchResultsSig;
|
|
}
|
|
|
|
if (selector === selectIsSearching) {
|
|
return signal(false);
|
|
}
|
|
|
|
if (selector === selectSavedRooms) {
|
|
return savedSig;
|
|
}
|
|
|
|
if (selector === selectAllUsers) {
|
|
return usersSig;
|
|
}
|
|
|
|
if (selector === selectCurrentUser) {
|
|
return currentUserSig;
|
|
}
|
|
|
|
return signal(null);
|
|
},
|
|
dispatch
|
|
} as unknown as Store;
|
|
const router = { navigate: vi.fn() } as unknown as Router;
|
|
const getFeaturedServers = vi.fn(() => of(options.featured ?? []));
|
|
const getTrendingServers = vi.fn(() => of(options.trending ?? []));
|
|
const serverDirectory = { getFeaturedServers, getTrendingServers } as unknown as ServerDirectoryFacade;
|
|
const friendIds = new Set<string>(options.friendIds ?? []);
|
|
const friendService = { friendIds: () => friendIds, friends: () => [] } as unknown as FriendService;
|
|
const injector = Injector.create({
|
|
providers: [
|
|
DashboardComponent,
|
|
{ provide: Store, useValue: store },
|
|
{ provide: Router, useValue: router },
|
|
{ provide: ServerDirectoryFacade, useValue: serverDirectory },
|
|
{ provide: FriendService, useValue: friendService },
|
|
{ provide: ViewportService, useValue: { isMobile: signal(options.isMobile ?? false) } }
|
|
]
|
|
});
|
|
const component = runInInjectionContext(injector, () => injector.get(DashboardComponent));
|
|
|
|
return {
|
|
component,
|
|
dispatch,
|
|
router,
|
|
getFeaturedServers,
|
|
getTrendingServers
|
|
};
|
|
}
|
|
|
|
describe('DashboardComponent', () => {
|
|
it('exposes the mobile viewport flag', () => {
|
|
expect(createHarness().component.isMobile()).toBe(false);
|
|
expect(createHarness({ isMobile: true }).component.isMobile()).toBe(true);
|
|
});
|
|
|
|
it('reports a new user when there are no servers or users', () => {
|
|
const { component } = createHarness();
|
|
|
|
expect(component.isNewUser()).toBe(true);
|
|
});
|
|
|
|
it('filters people by the active query', () => {
|
|
const { component } = createHarness({
|
|
users: [{ id: 'u1', displayName: 'Alice' } as unknown as User, { id: 'u2', displayName: 'Bob' } as unknown as User]
|
|
});
|
|
|
|
component.onSearchChange('ali');
|
|
|
|
expect(component.topPeopleResults().map((user) => user.id)).toEqual(['u1']);
|
|
});
|
|
|
|
it('limits server quick results', () => {
|
|
const { component } = createHarness({
|
|
searchResults: Array.from({ length: 8 }, (_, index) => makeServer(`s${index}`))
|
|
});
|
|
|
|
component.onSearchChange('s');
|
|
|
|
expect(component.topServerResults()).toHaveLength(5);
|
|
});
|
|
|
|
it('exposes an invite result for invite-like queries', () => {
|
|
const { component } = createHarness();
|
|
|
|
component.onSearchChange('https://app.test/invite/Code_42');
|
|
|
|
expect(component.inviteResult()).toBe('Code_42');
|
|
});
|
|
|
|
it('opens a joined server in place and routes others to the servers page', () => {
|
|
const joined = { id: 's1', name: 'Joined' } as unknown as Room;
|
|
const { component, dispatch, router } = createHarness({ saved: [joined] });
|
|
|
|
component.openServer(makeServer('s1', 'Joined'));
|
|
expect(dispatch).toHaveBeenCalledWith(RoomsActions.viewServer({ room: joined }));
|
|
|
|
component.openServer(makeServer('s2', 'Other'));
|
|
expect(router.navigate).toHaveBeenCalledWith(['/servers']);
|
|
});
|
|
|
|
it('navigates to the invite route when opening an invite', () => {
|
|
const { component, router } = createHarness();
|
|
|
|
component.onSearchChange('abc123');
|
|
component.openInvite();
|
|
|
|
expect(router.navigate).toHaveBeenCalledWith(['/invite', 'abc123']);
|
|
});
|
|
|
|
it('suggests people you might know independent of the query, excluding self', () => {
|
|
const { component } = createHarness({
|
|
users: [{ id: 'u1', oderId: 'u1', displayName: 'Alice' } as unknown as User, { id: 'u2', oderId: 'u2', displayName: 'Bob' } as unknown as User],
|
|
currentUser: { id: 'u1', oderId: 'u1' } as unknown as User
|
|
});
|
|
|
|
expect(component.peopleYouMightKnow().map((user) => user.id)).toEqual(['u2']);
|
|
});
|
|
|
|
it('loads popular servers from featured results on init', () => {
|
|
const featured = [makeServer('f1'), makeServer('f2')];
|
|
const { component } = createHarness({ featured });
|
|
|
|
component.ngOnInit();
|
|
|
|
expect(component.popularServers().map((server) => server.id)).toEqual(['f1', 'f2']);
|
|
});
|
|
|
|
it('falls back to trending servers when featured is empty', () => {
|
|
const trending = [makeServer('t1')];
|
|
const { component } = createHarness({ featured: [], trending });
|
|
|
|
component.ngOnInit();
|
|
|
|
expect(component.popularServers().map((server) => server.id)).toEqual(['t1']);
|
|
});
|
|
|
|
it('limits recently active servers to the discovery cap', () => {
|
|
const saved = Array.from({ length: 9 }, (_, index) => ({ id: `r${index}`, name: `Room ${index}`, userCount: 1 }) as unknown as Room);
|
|
const { component } = createHarness({ saved });
|
|
|
|
expect(component.recentlyActiveServers()).toHaveLength(5);
|
|
});
|
|
|
|
it('excludes existing friends from people you might know and lists them under friends', () => {
|
|
const { component } = createHarness({
|
|
users: [
|
|
{ id: 'u1', oderId: 'u1', displayName: 'Alice' } as unknown as User,
|
|
{ id: 'u2', oderId: 'u2', displayName: 'Bob' } as unknown as User,
|
|
{ id: 'u3', oderId: 'u3', displayName: 'Cara' } as unknown as User
|
|
],
|
|
currentUser: { id: 'u1', oderId: 'u1' } as unknown as User,
|
|
friendIds: ['u2']
|
|
});
|
|
|
|
expect(component.peopleYouMightKnow().map((user) => user.id)).toEqual(['u3']);
|
|
expect(component.friends().map((user) => user.id)).toEqual(['u2']);
|
|
});
|
|
|
|
it('records, removes, and clears recent searches', () => {
|
|
const { component } = createHarness();
|
|
|
|
component.onSearchChange('gaming');
|
|
component.submitSearch();
|
|
component.onSearchChange('music');
|
|
component.submitSearch();
|
|
|
|
expect(component.recentSearches()).toEqual(['music', 'gaming']);
|
|
|
|
component.removeRecentSearch('gaming');
|
|
expect(component.recentSearches()).toEqual(['music']);
|
|
|
|
component.clearRecentSearches();
|
|
expect(component.recentSearches()).toEqual([]);
|
|
});
|
|
|
|
it('deduplicates recent searches and keeps the most recent first', () => {
|
|
const { component } = createHarness();
|
|
|
|
component.onSearchChange('gaming');
|
|
component.submitSearch();
|
|
component.onSearchChange('music');
|
|
component.submitSearch();
|
|
component.onSearchChange('gaming');
|
|
component.submitSearch();
|
|
|
|
expect(component.recentSearches()).toEqual(['gaming', 'music']);
|
|
});
|
|
});
|