feat: Challenges Page and Challenge Solve Modal 1.03
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Injector, runInInjectionContext } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ChallengesApiService } from '../../frontend/src/app/features/challenges/challenges.service';
|
||||
import { ChallengeDetail } from '../../frontend/src/app/features/challenges/challenges.pure';
|
||||
|
||||
interface CapturedRequest {
|
||||
url: string;
|
||||
params: HttpParams;
|
||||
withCredentials: boolean;
|
||||
}
|
||||
|
||||
function makeDetail(): ChallengeDetail {
|
||||
return {
|
||||
challenge: {
|
||||
id: 'c1',
|
||||
name: 'alpha',
|
||||
descriptionMd: '# Alpha',
|
||||
categoryId: 'cat1',
|
||||
categoryAbbreviation: 'CRY',
|
||||
categoryIconPath: '',
|
||||
difficulty: 'LOW',
|
||||
livePoints: 90,
|
||||
solveCount: 1,
|
||||
solvedByMe: false,
|
||||
},
|
||||
solvers: [
|
||||
{
|
||||
position: 1,
|
||||
playerId: 'p1',
|
||||
playerName: 'alice',
|
||||
solvedAtUtc: '2025-01-01T00:00:00.000Z',
|
||||
awardedPoints: 100,
|
||||
basePoints: 100,
|
||||
rankBonus: 0,
|
||||
isFirst: true,
|
||||
isSecond: false,
|
||||
isThird: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
class StubHttpClient {
|
||||
public captured: CapturedRequest | null = null;
|
||||
|
||||
get<T>(url: string, options: { withCredentials?: boolean; params?: HttpParams | Record<string, string> }): Observable<T> {
|
||||
this.captured = {
|
||||
url,
|
||||
params:
|
||||
options.params instanceof HttpParams
|
||||
? options.params
|
||||
: new HttpParams({ fromObject: (options.params ?? {}) as Record<string, string> }),
|
||||
withCredentials: !!options.withCredentials,
|
||||
};
|
||||
return new Observable<T>((subscriber) => {
|
||||
subscriber.next(makeDetail() as unknown as T);
|
||||
subscriber.complete();
|
||||
});
|
||||
}
|
||||
|
||||
post<T>(_url: string, _body: unknown, _options: unknown): Observable<T> {
|
||||
return new Observable<T>();
|
||||
}
|
||||
}
|
||||
|
||||
describe('ChallengesApiService.getDetail', () => {
|
||||
let http: StubHttpClient;
|
||||
let service: ChallengesApiService;
|
||||
|
||||
beforeEach(() => {
|
||||
http = new StubHttpClient();
|
||||
const injector = Injector.create({
|
||||
providers: [
|
||||
{ provide: HttpClient, useValue: http },
|
||||
ChallengesApiService,
|
||||
],
|
||||
});
|
||||
service = runInInjectionContext(injector, () => injector.get(ChallengesApiService));
|
||||
});
|
||||
|
||||
it('requests the challenge detail with include=solvers and withCredentials', (done) => {
|
||||
service.getDetail('c1').subscribe(() => {
|
||||
try {
|
||||
expect(http.captured).not.toBeNull();
|
||||
expect(http.captured!.url).toBe('/api/v1/challenges/c1');
|
||||
expect(http.captured!.params.get('include')).toBe('solvers');
|
||||
expect(http.captured!.withCredentials).toBe(true);
|
||||
done();
|
||||
} catch (err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes the challenge id in the URL path', (done) => {
|
||||
service.getDetail('c/with spaces').subscribe(() => {
|
||||
try {
|
||||
expect(http.captured!.url).toBe('/api/v1/challenges/c%2Fwith%20spaces');
|
||||
expect(http.captured!.params.get('include')).toBe('solvers');
|
||||
done();
|
||||
} catch (err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user