From b9c25369c7fd2acf24af132eeda676e440563203 Mon Sep 17 00:00:00 2001 From: TO Date: Sun, 9 Nov 2025 00:00:36 +0100 Subject: [PATCH] prettier --- TESTING.md | 16 ++ TEST_COVERAGE.md | 17 ++ .../champion-randomizer.component.css | 2 +- .../champion-randomizer.component.html | 24 +- .../champion-randomizer.component.spec.ts | 10 +- .../champion-randomizer.component.ts | 10 +- .../control-header.component.html | 24 +- .../lane-card/lane-card.component.css | 3 +- .../lane-card/lane-card.component.html | 208 +++++++++--------- .../directives/preload-link.directive.spec.ts | 8 +- .../image-cache.interceptor.spec.ts | 2 +- .../services/champion-data.service.spec.ts | 2 +- src/app/services/champion-data.service.ts | 6 +- .../services/image-preload.service.spec.ts | 2 +- src/app/services/random-number.spec.ts | 2 +- 15 files changed, 189 insertions(+), 147 deletions(-) diff --git a/TESTING.md b/TESTING.md index 8297bb7..8117577 100644 --- a/TESTING.md +++ b/TESTING.md @@ -3,6 +3,7 @@ ## Quick Start Run all tests: + ```bash npm test ``` @@ -31,25 +32,30 @@ src/app/ ## What's Tested ### Components (4) + - ✅ **App** - Main application component - ✅ **ChampionRandomizerComponent** - Core randomization logic - ✅ **ControlHeaderComponent** - User controls - ✅ **LaneCardComponent** - Individual lane display ### Services (3) + - ✅ **ChampionDataService** - Data fetching and caching - ✅ **ImagePreloadService** - Image preloading - ✅ **RandomNumber** - Secure random generation ### Directives (1) + - ✅ **PreloadLinkDirective** - Link preloading ### Interceptors (1) + - ✅ **imageCacheInterceptor** - HTTP caching ## Key Test Scenarios ### Champion Randomizer + - Random champion assignment for each lane - Lane enable/disable functionality - Reroll system with bank management @@ -58,6 +64,7 @@ src/app/ - Copy draft to clipboard ### Champion Data Service + - API data fetching - LocalStorage caching (24-hour expiry) - Cache validation and invalidation @@ -65,11 +72,13 @@ src/app/ - Champion filtering and sorting ### Random Number Service + - Cryptographically secure random numbers - Range validation - Edge case handling ### Image Management + - Cache control headers - Image preloading - Duplicate prevention @@ -78,6 +87,7 @@ src/app/ ## Test Configuration The project uses: + - **Framework**: Jasmine - **Runner**: Karma - **Browser**: Chrome (or ChromeHeadless for CI) @@ -85,6 +95,7 @@ The project uses: ## Continuous Integration For CI environments without a display: + ```bash npm test -- --watch=false --browsers=ChromeHeadless ``` @@ -92,6 +103,7 @@ npm test -- --watch=false --browsers=ChromeHeadless ## Code Coverage Generate coverage report: + ```bash ng test --no-watch --code-coverage ``` @@ -110,20 +122,24 @@ Coverage report will be available in `coverage/` directory. ## Troubleshooting ### Tests won't run + - Ensure all dependencies are installed: `npm install` - Check that Chrome is installed for Karma ### Specific test failing + - Run tests in watch mode: `npm test` - Check console output for specific error messages ### Coverage not generating + - Ensure you're using the `--code-coverage` flag - Check that all test files end with `.spec.ts` ## Contributing When adding new features: + 1. Write tests first (TDD approach) 2. Ensure all tests pass 3. Maintain high code coverage diff --git a/TEST_COVERAGE.md b/TEST_COVERAGE.md index 8580ec5..8bc792a 100644 --- a/TEST_COVERAGE.md +++ b/TEST_COVERAGE.md @@ -5,12 +5,14 @@ This document provides an overview of all the tests created for the Random LOL a ## Test Files Created/Updated ### 1. **App Component Tests** (`src/app/app.spec.ts`) + - ✅ Component creation - ✅ ImagePreloadService injection - ✅ Champion randomizer component rendering - ✅ Toolbar rendering ### 2. **Champion Randomizer Component Tests** (`src/app/components/champion-randomizer.component.spec.ts`) + - ✅ Component creation and initialization - ✅ Lane management (enable/disable lanes) - ✅ Champion assignment and rolling @@ -26,6 +28,7 @@ This document provides an overview of all the tests created for the Random LOL a - ✅ Shift key tracking for force rerolls ### 3. **Control Header Component Tests** (`src/app/components/control-header/control-header.component.spec.ts`) + - ✅ Component creation - ✅ All input properties (isLoading, hasChampions, loadError, canCopyDraft, rerollBank, rerollBankMax) - ✅ Output events (roll, retry, copyDraft) @@ -33,6 +36,7 @@ This document provides an overview of all the tests created for the Random LOL a - ✅ Template rendering (spinner, error messages) ### 4. **Lane Card Component Tests** (`src/app/components/lane-card/lane-card.component.spec.ts`) + - ✅ Component creation - ✅ All input properties (lane, laneLabel, champion, canReRoll, isLoading, hasChampions, isShiftPressed, disabled) - ✅ Output events (changeChampion, toggleDisableLane) @@ -40,6 +44,7 @@ This document provides an overview of all the tests created for the Random LOL a - ✅ Template rendering (champion name, image, lane label, role chips, disabled styling) ### 5. **Champion Data Service Tests** (`src/app/services/champion-data.service.spec.ts`) + - ✅ Service creation - ✅ Loading champions from API - ✅ HTTP to HTTPS URL conversion @@ -58,11 +63,13 @@ This document provides an overview of all the tests created for the Random LOL a - ✅ Invalid cache data handling ### 6. **Image Preload Service Tests** (`src/app/services/image-preload.service.spec.ts`) + - ✅ Service creation - ✅ Cache clearing functionality - ✅ Error handling during cache operations ### 7. **Random Number Service Tests** (`src/app/services/random-number.spec.ts`) + - ✅ Service creation - ✅ Random number generation within range - ✅ Edge case: max = 1 @@ -73,12 +80,14 @@ This document provides an overview of all the tests created for the Random LOL a - ✅ crypto.getRandomValues usage verification ### 8. **Preload Link Directive Tests** (`src/app/directives/preload-link.directive.spec.ts`) + - ✅ Directive creation - ✅ Preload link addition to document head - ✅ Fetchpriority attribute setting - ✅ Duplicate link prevention ### 9. **Image Cache Interceptor Tests** (`src/app/interceptors/image-cache.interceptor.spec.ts`) + - ✅ Cache header addition for PNG images - ✅ Cache header addition for JPG images - ✅ Cache header addition for JPEG images @@ -93,6 +102,7 @@ This document provides an overview of all the tests created for the Random LOL a ## Test Statistics ### Total Test Suites: 9 + - App: 4 tests - Champion Randomizer: 29 tests - Control Header: 10 tests @@ -108,20 +118,24 @@ This document provides an overview of all the tests created for the Random LOL a ## Coverage Areas ### ✅ Components (100%) + - App - ChampionRandomizerComponent - ControlHeaderComponent - LaneCardComponent ### ✅ Services (100%) + - ChampionDataService - ImagePreloadService - RandomNumber ### ✅ Directives (100%) + - PreloadLinkDirective ### ✅ Interceptors (100%) + - imageCacheInterceptor ## Key Testing Features @@ -138,16 +152,19 @@ This document provides an overview of all the tests created for the Random LOL a ## Running the Tests To run all tests: + ```bash npm test ``` To run tests in headless mode (requires Chrome): + ```bash npm test -- --watch=false --browsers=ChromeHeadless ``` To run tests with coverage: + ```bash ng test --no-watch --code-coverage ``` diff --git a/src/app/components/champion-randomizer.component.css b/src/app/components/champion-randomizer.component.css index e5d48b3..8965c40 100644 --- a/src/app/components/champion-randomizer.component.css +++ b/src/app/components/champion-randomizer.component.css @@ -23,4 +23,4 @@ .warning-card mat-icon { font-size: 1.25rem; -} \ No newline at end of file +} diff --git a/src/app/components/champion-randomizer.component.html b/src/app/components/champion-randomizer.component.html index 43bc5cc..f4465bd 100644 --- a/src/app/components/champion-randomizer.component.html +++ b/src/app/components/champion-randomizer.component.html @@ -12,18 +12,18 @@ >
@for (assignment of laneAssignments(); track assignment.lane) { - + }
diff --git a/src/app/components/champion-randomizer.component.spec.ts b/src/app/components/champion-randomizer.component.spec.ts index 3e79fb0..5ee4691 100644 --- a/src/app/components/champion-randomizer.component.spec.ts +++ b/src/app/components/champion-randomizer.component.spec.ts @@ -66,7 +66,7 @@ describe('ChampionRandomizerComponent', () => { championsByLane: championsByLaneSignal.asReadonly(), loading: loadingSignal.asReadonly(), error: errorSignal.asReadonly(), - } + }, ); championDataSpy.ensureLoaded.and.returnValue(Promise.resolve()); @@ -83,7 +83,7 @@ describe('ChampionRandomizerComponent', () => { }).compileComponents(); championDataService = TestBed.inject( - ChampionDataService + ChampionDataService, ) as jasmine.SpyObj; fixture = TestBed.createComponent(ChampionRandomizerComponent); component = fixture.componentInstance; @@ -230,7 +230,7 @@ describe('ChampionRandomizerComponent', () => { ['mid', null], ['adc', null], ['support', null], - ]) + ]), ); // Set reroll bank to 0.5 (less than 1) @@ -286,7 +286,7 @@ describe('ChampionRandomizerComponent', () => { ['mid', mockChampions[1]], ['adc', mockChampions[2]], ['support', mockChampions[3]], - ]) + ]), ); expect(component['canCopyDraft']()).toBe(true); @@ -339,7 +339,7 @@ describe('ChampionRandomizerComponent', () => { } spyOn(navigator.clipboard, 'writeText').and.returnValue( - Promise.reject(new Error('Clipboard error')) + Promise.reject(new Error('Clipboard error')), ); spyOn(console, 'error'); component['rollAssignments'](); diff --git a/src/app/components/champion-randomizer.component.ts b/src/app/components/champion-randomizer.component.ts index 0b2526c..6e77fb2 100644 --- a/src/app/components/champion-randomizer.component.ts +++ b/src/app/components/champion-randomizer.component.ts @@ -63,7 +63,7 @@ export class ChampionRandomizerComponent { protected readonly hasChampions = computed(() => this.champions().length > 0); protected readonly assignments = signal>( - this.createEmptyAssignments() + this.createEmptyAssignments(), ); protected readonly disabledLanesArray = computed(() => { @@ -104,8 +104,8 @@ export class ChampionRandomizerComponent { (champion) => !this.assignmentsArray().some( (assignedChampion) => - assignedChampion && assignedChampion.name === champion.name - ) + assignedChampion && assignedChampion.name === champion.name, + ), ) || []; notUsed.set(lane, champion); } @@ -125,7 +125,7 @@ export class ChampionRandomizerComponent { champion, rolesText, } satisfies LaneAssignmentView; - }) + }), ); protected readonly canCopyDraft = computed(() => { @@ -185,7 +185,7 @@ export class ChampionRandomizerComponent { for (const lane of this.activatedLanesArray()) { const candidates = (championsByLaneMap.get(lane) || []).filter( - (champion: Champion) => !selected.has(champion.name) + (champion: Champion) => !selected.has(champion.name), ); if (candidates.length === 0) { diff --git a/src/app/components/control-header/control-header.component.html b/src/app/components/control-header/control-header.component.html index bc4c571..df6ca9b 100644 --- a/src/app/components/control-header/control-header.component.html +++ b/src/app/components/control-header/control-header.component.html @@ -36,18 +36,18 @@ {{ rerollBank() }} @if (isLoading()) { -
- - Loading champion data… -
+
+ + Loading champion data… +
} @else if (loadError(); as errorMessage) { -
- - {{ errorMessage }} - -
+
+ + {{ errorMessage }} + +
} diff --git a/src/app/components/lane-card/lane-card.component.css b/src/app/components/lane-card/lane-card.component.css index 341a246..87b7544 100644 --- a/src/app/components/lane-card/lane-card.component.css +++ b/src/app/components/lane-card/lane-card.component.css @@ -72,7 +72,6 @@ } @keyframes pulse { - 0%, 100% { opacity: 1; @@ -132,4 +131,4 @@ .enable-lane-button { margin-left: auto; -} \ No newline at end of file +} diff --git a/src/app/components/lane-card/lane-card.component.html b/src/app/components/lane-card/lane-card.component.html index fcba542..3c4c595 100644 --- a/src/app/components/lane-card/lane-card.component.html +++ b/src/app/components/lane-card/lane-card.component.html @@ -1,104 +1,114 @@ @if (disabled()) { - - - - {{ laneLabel() }} - Lane currently disabled - - - -
- -

This lane has been disabled

-

You chose to exclude this lane from the random selection.

-
- -
-
+ + + + {{ laneLabel() }} + Lane currently disabled + + + +
+ +

This lane has been disabled

+

+ You chose to exclude this lane from the random selection. +

+
+ +
+
} @else { - - - - {{ laneLabel() }} - - {{ champion() ? 'Unique pick locked in' : 'Champion needed for this lane' }} - - - - - - @if (champion(); as champ) { -
- -

{{ champ.name }}

-
- - @for (role of champ.roles; track role) { - {{ roleLabel(role) }} - } - - } @else { -

No champion available

-

Roll again once champions finish loading.

- } @if (canReRoll()) { - - } @else { - + + + @if (champion(); as champ) { +
+ +

{{ champ.name }}

+
+ + @for (role of champ.roles; track role) { + {{ roleLabel(role) }} + } + } @else { - - } @if (!isShiftPressed()) { -

No re-rolls left

- } @else { -

Force Re-Roll!

+

+ No champion available +

+

+ Roll again once champions finish loading. +

} - - } -
-
+ @if (canReRoll()) { + + } @else { + + } + + } diff --git a/src/app/directives/preload-link.directive.spec.ts b/src/app/directives/preload-link.directive.spec.ts index 760c715..c6822af 100644 --- a/src/app/directives/preload-link.directive.spec.ts +++ b/src/app/directives/preload-link.directive.spec.ts @@ -34,7 +34,7 @@ describe('PreloadLinkDirective', () => { setTimeout(() => { const preloadLinks = document.querySelectorAll('link[rel="preload"][as="image"]'); const hasTestLink = Array.from(preloadLinks).some((link) => - link.getAttribute('href')?.includes('test.png') + link.getAttribute('href')?.includes('test.png'), ); expect(hasTestLink).toBe(true); done(); @@ -47,10 +47,10 @@ describe('PreloadLinkDirective', () => { setTimeout(() => { const preloadLinks = document.querySelectorAll( - 'link[rel="preload"][as="image"][fetchpriority="high"]' + 'link[rel="preload"][as="image"][fetchpriority="high"]', ); const hasTestLink = Array.from(preloadLinks).some((link) => - link.getAttribute('href')?.includes('test.png') + link.getAttribute('href')?.includes('test.png'), ); expect(hasTestLink).toBe(true); done(); @@ -66,7 +66,7 @@ describe('PreloadLinkDirective', () => { setTimeout(() => { const preloadLinks = document.querySelectorAll( - 'link[rel="preload"][href="https://example.com/test.png"]' + 'link[rel="preload"][href="https://example.com/test.png"]', ); // Should only have one link, not duplicates expect(preloadLinks.length).toBeLessThanOrEqual(1); diff --git a/src/app/interceptors/image-cache.interceptor.spec.ts b/src/app/interceptors/image-cache.interceptor.spec.ts index 4e0ea43..91cbe7c 100644 --- a/src/app/interceptors/image-cache.interceptor.spec.ts +++ b/src/app/interceptors/image-cache.interceptor.spec.ts @@ -79,7 +79,7 @@ describe('imageCacheInterceptor', () => { .subscribe(); const req = httpMock.expectOne( - 'https://ddragon.leagueoflegends.com/cdn/14.1.1/img/champion/Aatrox.png' + 'https://ddragon.leagueoflegends.com/cdn/14.1.1/img/champion/Aatrox.png', ); expect(req.request.headers.get('Cache-Control')).toBe('public, max-age=86400, immutable'); req.flush({}); diff --git a/src/app/services/champion-data.service.spec.ts b/src/app/services/champion-data.service.spec.ts index c0789f9..f70ab86 100644 --- a/src/app/services/champion-data.service.spec.ts +++ b/src/app/services/champion-data.service.spec.ts @@ -194,7 +194,7 @@ describe('ChampionDataService', () => { const champions = service.champions(); for (let i = 1; i < champions.length; i++) { expect(champions[i - 1].name.localeCompare(champions[i].name)).toBeLessThanOrEqual( - 0 + 0, ); } }); diff --git a/src/app/services/champion-data.service.ts b/src/app/services/champion-data.service.ts index b4326b7..f1a58fc 100644 --- a/src/app/services/champion-data.service.ts +++ b/src/app/services/champion-data.service.ts @@ -134,7 +134,7 @@ export class ChampionDataService { try { const response = await firstValueFrom( - this.http.get>(CHAMPION_DATA_URL) + this.http.get>(CHAMPION_DATA_URL), ); const champions = Object.entries(response) @@ -174,8 +174,8 @@ export class ChampionDataService { new Set( data.positions .map((position) => POSITION_TO_LANE[position]) - .filter((role): role is Lane => role !== undefined) - ) + .filter((role): role is Lane => role !== undefined), + ), ); if (roles.length === 0 || !data.icon) { diff --git a/src/app/services/image-preload.service.spec.ts b/src/app/services/image-preload.service.spec.ts index f6aec23..8d763a6 100644 --- a/src/app/services/image-preload.service.spec.ts +++ b/src/app/services/image-preload.service.spec.ts @@ -40,7 +40,7 @@ describe('ImagePreloadService', () => { }); championDataService = TestBed.inject( - ChampionDataService + ChampionDataService, ) as jasmine.SpyObj; service = TestBed.inject(ImagePreloadService); }); diff --git a/src/app/services/random-number.spec.ts b/src/app/services/random-number.spec.ts index 3b652ce..c0cf7bb 100644 --- a/src/app/services/random-number.spec.ts +++ b/src/app/services/random-number.spec.ts @@ -37,7 +37,7 @@ describe('RandomNumber', () => { it('should throw RangeError for max > Number.MAX_SAFE_INTEGER', () => { expect(() => RandomNumber.getSecureRandomInt(Number.MAX_SAFE_INTEGER + 1)).toThrowError( - RangeError + RangeError, ); });