diff --git a/.coverage b/.coverage deleted file mode 100644 index 1e3f2c2..0000000 Binary files a/.coverage and /dev/null differ diff --git a/.gitignore b/.gitignore index 9b7993a..a6d15e8 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,6 @@ api-key.json music/* uploads/* static/* -backend/app.db \ No newline at end of file +backend/app.dbcoverage/ +test-results/ +.coverage diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..850a5a7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Jarian Cottingham + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 818e89c..f91aae4 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,48 @@ Key settings: - `web/` — React web frontend (Vite + Tailwind) - `mobile/` — React Native mobile app (Expo) - `shared/` — Shared TypeScript types and API client +- `e2e/` — Playwright end-to-end specs +- `docs/` — Implementation plan + +## Tests + +**Backend** (pytest, 90%+ coverage gate): + +```bash +cd backend +pip install -r requirements.txt pytest-cov +pytest +``` + +300 tests covering endpoints, routers, services, schemas, and models. + +**E2E** (Playwright): + +```bash +npm install +npx playwright install +npm run dev:backend & # start backend on :8000 +npx playwright test +``` + +**TypeScript** (web + mobile + shared workspaces): + +```bash +npm install +npm run typecheck +npm run lint +``` + +## Development + +npm workspaces scripts at the repo root: + +```bash +npm run dev # web + backend together +npm run dev:web # Vite dev server only +npm run build:web # production web build +``` + +## License + +MIT — see [LICENSE](LICENSE). diff --git a/backend/app/main.py b/backend/app/main.py index 888941a..f8a957a 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,10 +1,8 @@ import os -import functools from fastapi import FastAPI, Request, HTTPException, Depends from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from starlette.middleware.base import BaseHTTPMiddleware -import os from .db.database import init_db from .routers import ( @@ -84,7 +82,6 @@ app.include_router(account.router) @app.on_event("startup") def startup(): init_db() - from sqlalchemy.orm import Session from .db.database import SessionLocal db = SessionLocal() try: diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index f517198..3d068cd 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -6,4 +6,20 @@ from .radio import RadioStation from .shareplay import SharePlayRoom, SharePlayCue from .settings import UserSetting from .releases import NewReleaseCheck -from .events import ConcertEvent \ No newline at end of file +from .events import ConcertEvent + +__all__ = [ + "Song", + "Playlist", + "PlaylistSong", + "MoodCategory", + "MoodSong", + "LyricsCache", + "LofiChannel", + "RadioStation", + "SharePlayRoom", + "SharePlayCue", + "UserSetting", + "NewReleaseCheck", + "ConcertEvent", +] \ No newline at end of file diff --git a/backend/app/models/events.py b/backend/app/models/events.py index 463d7cf..eb8c134 100644 --- a/backend/app/models/events.py +++ b/backend/app/models/events.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, String, Float, DateTime +from sqlalchemy import Column, String, Float from ..db.database import Base class ConcertEvent(Base): diff --git a/backend/app/routers/account.py b/backend/app/routers/account.py index 8078e2c..5a4cf3a 100644 --- a/backend/app/routers/account.py +++ b/backend/app/routers/account.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Depends from sqlalchemy.orm import Session from ..db.database import get_db -from ..schemas.account import AccountStatsResponse, ListeningHistoryItem +from ..schemas.account import AccountStatsResponse from ..models.song import Song from ..models.playlist import Playlist diff --git a/backend/app/routers/events.py b/backend/app/routers/events.py index 828c862..2008e4a 100644 --- a/backend/app/routers/events.py +++ b/backend/app/routers/events.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter from typing import List, Optional from ..schemas.events import ConcertEventResponse diff --git a/backend/app/routers/import_.py b/backend/app/routers/import_.py index 08688fe..b57c34d 100644 --- a/backend/app/routers/import_.py +++ b/backend/app/routers/import_.py @@ -1,9 +1,9 @@ -from fastapi import APIRouter, Depends, HTTPException, UploadFile, File +from fastapi import APIRouter, Depends, UploadFile, File from sqlalchemy.orm import Session from typing import List from ..db.database import get_db from ..schemas.song import ScanResult -from ..services.audio import scan_directory, extract_metadata, transcode_to_ogg +from ..services.audio import extract_metadata, transcode_to_ogg from ..models.song import Song import uuid import os diff --git a/backend/app/routers/lofi.py b/backend/app/routers/lofi.py index dbf1abb..c24b088 100644 --- a/backend/app/routers/lofi.py +++ b/backend/app/routers/lofi.py @@ -25,7 +25,7 @@ DEFAULT_CHANNELS = [ @router.get("/channels", response_model=List[LofiChannelResponse]) def list_channels(db: Session = Depends(get_db)): - channels = db.query(LofiChannel).filter(LofiChannel.is_active == True).all() + channels = db.query(LofiChannel).filter(LofiChannel.is_active.is_(True)).all() return [LofiChannelResponse.model_validate(c) for c in channels] diff --git a/backend/app/routers/mood.py b/backend/app/routers/mood.py index b682ace..c7a8716 100644 --- a/backend/app/routers/mood.py +++ b/backend/app/routers/mood.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException, Query, Body +from fastapi import APIRouter, Depends, Query from sqlalchemy.orm import Session from typing import List, Optional from pydantic import BaseModel @@ -6,7 +6,6 @@ from ..db.database import get_db from ..models.mood import MoodCategory from ..schemas.mood import MoodCategoryResponse, MoodPlaylistResponse from ..services.mood_engine import analyze_song_mood, get_mood_playlist, seed_mood_categories -from ..services.audio import get_stream_path class SetMoodRequest(BaseModel): diff --git a/backend/app/routers/playlists.py b/backend/app/routers/playlists.py index 2912dfe..ef728a2 100644 --- a/backend/app/routers/playlists.py +++ b/backend/app/routers/playlists.py @@ -1,6 +1,6 @@ -from fastapi import APIRouter, Depends, HTTPException, Query +from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session -from typing import List, Optional +from typing import List from ..db.database import get_db from ..models.playlist import Playlist, PlaylistSong from ..models.song import Song diff --git a/backend/app/routers/radio.py b/backend/app/routers/radio.py index 6afd15f..f35cc8b 100644 --- a/backend/app/routers/radio.py +++ b/backend/app/routers/radio.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Query from fastapi.responses import StreamingResponse from typing import List, Optional import httpx diff --git a/backend/app/routers/releases.py b/backend/app/routers/releases.py index f172bd0..64290d5 100644 --- a/backend/app/routers/releases.py +++ b/backend/app/routers/releases.py @@ -1,9 +1,8 @@ -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends from sqlalchemy.orm import Session from typing import List, Optional from ..db.database import get_db from ..models.song import Song -from ..models.releases import NewReleaseCheck from ..schemas.releases import NewReleaseResponse from ..services.musicbrainz import search_artist, get_artist_releases diff --git a/backend/app/routers/settings.py b/backend/app/routers/settings.py index 5523e4a..65d69e4 100644 --- a/backend/app/routers/settings.py +++ b/backend/app/routers/settings.py @@ -1,3 +1,4 @@ +import json from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from typing import List, Optional @@ -97,7 +98,4 @@ def remove_server(server_id: str, db: Session = Depends(get_db)): setting.value = json.dumps(dirs) db.commit() - return {"message": "Server removed"} - - -import json \ No newline at end of file + return {"message": "Server removed"} \ No newline at end of file diff --git a/backend/app/routers/shareplay.py b/backend/app/routers/shareplay.py index 2ecce60..ef24916 100644 --- a/backend/app/routers/shareplay.py +++ b/backend/app/routers/shareplay.py @@ -4,8 +4,7 @@ from sqlalchemy.orm import Session from typing import Optional from pydantic import BaseModel from ..db.database import get_db -from ..models.shareplay import SharePlayRoom, SharePlayCue as SharePlayCueModel -from ..schemas.shareplay import SharePlayRoomResponse, SharePlayCommand, SharePlayCueResponse +from ..schemas.shareplay import SharePlayCueResponse from ..services.shareplay import SharePlayManager @@ -112,7 +111,7 @@ async def websocket_endpoint(websocket: WebSocket, room_id: str): # pragma: no "type": "playback_update", "data": {"is_playing": True} }) - except: + except Exception: pass elif state and cmd.get("type") == "pause": manager.update_state(room_id, is_playing=False) @@ -122,7 +121,7 @@ async def websocket_endpoint(websocket: WebSocket, room_id: str): # pragma: no "type": "playback_update", "data": {"is_playing": False} }) - except: + except Exception: pass await websocket.send_json({"type": "ack", "data": message}) @@ -136,7 +135,7 @@ async def websocket_endpoint(websocket: WebSocket, room_id: str): # pragma: no "type": "chat", "data": chat_msg }) - except: + except Exception: pass elif message.get("type") == "seek": @@ -148,7 +147,7 @@ async def websocket_endpoint(websocket: WebSocket, room_id: str): # pragma: no "type": "seek_update", "data": {"position": pos} }) - except: + except Exception: pass except WebSocketDisconnect: diff --git a/backend/app/schemas/__init__.py b/backend/app/schemas/__init__.py index cd05604..849513d 100644 --- a/backend/app/schemas/__init__.py +++ b/backend/app/schemas/__init__.py @@ -9,4 +9,32 @@ from .account import AccountStatsResponse, ListeningHistoryItem from .releases import NewReleaseResponse, ReleaseAlbumResponse from .events import ConcertEventResponse from .search import SearchResultResponse -from .common import PaginatedResponse \ No newline at end of file +from .common import PaginatedResponse +__all__ = [ + "SongBase", + "SongCreate", + "SongResponse", + "ScanResult", + "PlaylistBase", + "PlaylistCreate", + "PlaylistResponse", + "PlaylistWithSongs", + "MoodCategoryResponse", + "MoodAnalysisResponse", + "MoodPlaylistResponse", + "RadioStationResponse", + "RadioCurrentResponse", + "LofiChannelResponse", + "SharePlayRoomResponse", + "SharePlayCommand", + "SharePlayCueResponse", + "UserSettingsResponse", + "ServerConfigResponse", + "AccountStatsResponse", + "ListeningHistoryItem", + "NewReleaseResponse", + "ReleaseAlbumResponse", + "ConcertEventResponse", + "SearchResultResponse", + "PaginatedResponse", +] diff --git a/backend/app/schemas/mood.py b/backend/app/schemas/mood.py index bf1d016..c9f8c8c 100644 --- a/backend/app/schemas/mood.py +++ b/backend/app/schemas/mood.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, ConfigDict -from typing import Optional, List, Dict +from typing import List from datetime import datetime from .song import SongResponse diff --git a/backend/app/services/audio.py b/backend/app/services/audio.py index 2114bb1..0f78cf0 100644 --- a/backend/app/services/audio.py +++ b/backend/app/services/audio.py @@ -1,7 +1,6 @@ import os import uuid import subprocess -import shutil from typing import Optional, Dict, Any from sqlalchemy.orm import Session from ..models.song import Song @@ -21,7 +20,6 @@ def extract_metadata(file_path: str) -> Dict[str, Any]: # pragma: no cover from mutagen.oggvorbis import OggVorbis from mutagen.wave import WAVE from mutagen.mp4 import MP4 - from mutagen.id3 import ID3 metadata: Dict[str, Any] = {} ext = os.path.splitext(file_path)[1].lower() diff --git a/backend/app/services/mood_engine.py b/backend/app/services/mood_engine.py index ec90efc..5bff8a5 100644 --- a/backend/app/services/mood_engine.py +++ b/backend/app/services/mood_engine.py @@ -1,5 +1,4 @@ import json -import os from typing import Dict, List, Optional, Tuple from sqlalchemy.orm import Session from ..models.song import Song diff --git a/backend/tests/test_deep_coverage.py b/backend/tests/test_deep_coverage.py index bba5ae5..d9ce42b 100644 --- a/backend/tests/test_deep_coverage.py +++ b/backend/tests/test_deep_coverage.py @@ -1,5 +1,5 @@ """Tests for deep coverage of services and middleware.""" -from unittest.mock import MagicMock, patch, AsyncMock +from unittest.mock import MagicMock, patch import tempfile import os diff --git a/backend/tests/test_endpoints.py b/backend/tests/test_endpoints.py index d6cf8c0..95f73f3 100644 --- a/backend/tests/test_endpoints.py +++ b/backend/tests/test_endpoints.py @@ -1,12 +1,10 @@ #!/usr/bin/env python3 """Comprehensive backend endpoint tests for music-app.""" -import json import httpx import sys import os import time import subprocess -import signal BASE_URL = "http://localhost:8000" passed = 0 @@ -25,13 +23,14 @@ def test(name, condition, detail=""): def start_server(): """Start the backend server.""" - os.chdir("/home/user/playground/music-app/backend") + # backend/ directory (parent of this tests/ dir) + os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # Remove old DB if os.path.exists("app.db"): os.remove("app.db") - + proc = subprocess.Popen( - ["venv/bin/python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"], + [sys.executable, "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) @@ -285,7 +284,7 @@ def main(): print(f"\n📊 Results: {passed}/{total} passed, {failed} failed") if errors: - print(f"\n❌ Failed tests:") + print("\n❌ Failed tests:") for e in errors: print(f" - {e}") diff --git a/backend/tests/test_routers_lofi.py b/backend/tests/test_routers_lofi.py index d5ac3c6..267bf33 100644 --- a/backend/tests/test_routers_lofi.py +++ b/backend/tests/test_routers_lofi.py @@ -1,6 +1,4 @@ """Router tests - LoFi endpoints.""" -from app.models.lofi import LofiChannel -from app.db.database import SessionLocal class TestListChannels: diff --git a/backend/tests/test_routers_shareplay.py b/backend/tests/test_routers_shareplay.py index d54bc0e..6722a67 100644 --- a/backend/tests/test_routers_shareplay.py +++ b/backend/tests/test_routers_shareplay.py @@ -1,7 +1,4 @@ """Router tests - SharePlay endpoints.""" -from app.models.song import Song -from app.models.shareplay import SharePlayRoom -from app.db.database import SessionLocal class TestCreateRoom: diff --git a/backend/tests/test_services.py b/backend/tests/test_services.py index ddda1f3..5c2e9ec 100644 --- a/backend/tests/test_services.py +++ b/backend/tests/test_services.py @@ -2,7 +2,6 @@ import pytest import sys import os -import json import tempfile import shutil diff --git a/backend/tests/test_services_advanced.py b/backend/tests/test_services_advanced.py index 879bd7e..ad22fbc 100644 --- a/backend/tests/test_services_advanced.py +++ b/backend/tests/test_services_advanced.py @@ -1,6 +1,4 @@ """Extended mood engine and SharePlay service tests.""" -from unittest.mock import MagicMock, patch -from sqlalchemy.orm import Session class TestAnalyzeLyrics: @@ -11,7 +9,7 @@ class TestAnalyzeLyrics: assert len(scores) == len(MOOD_NAMES) def test_none_input(self): - from app.services.mood_engine import analyze_lyrics, MOOD_NAMES + from app.services.mood_engine import analyze_lyrics scores = analyze_lyrics(None) assert all(v == 0.0 for v in scores.values()) diff --git a/backend/tests/test_services_extended.py b/backend/tests/test_services_extended.py index 8d0760f..f32b2b9 100644 --- a/backend/tests/test_services_extended.py +++ b/backend/tests/test_services_extended.py @@ -31,7 +31,6 @@ class TestTranscode: class TestScanDirectory: def test_empty_dir(self): from app.services.audio import scan_directory - from unittest.mock import MagicMock with tempfile.TemporaryDirectory() as tmpdir: mock_db = MagicMock() mock_db.query.return_value.first.return_value = None @@ -42,7 +41,6 @@ class TestScanDirectory: def test_skips_non_audio(self): from app.services.audio import scan_directory - from unittest.mock import MagicMock with tempfile.TemporaryDirectory() as tmpdir: with open(os.path.join(tmpdir, "readme.txt"), "w") as f: f.write("test") @@ -55,7 +53,6 @@ class TestScanDirectory: class TestDeleteSong: def test_delete_files(self): from app.services.audio import delete_song - from unittest.mock import MagicMock tmpdir = tempfile.mkdtemp() f1 = os.path.join(tmpdir, "t1.ogg") f2 = os.path.join(tmpdir, "t2.mp3") @@ -73,7 +70,6 @@ class TestDeleteSong: def test_delete_no_paths(self): from app.services.audio import delete_song - from unittest.mock import MagicMock song = MagicMock() song.file_path = None song.transcoded_path = None @@ -84,7 +80,6 @@ class TestDeleteSong: class TestGetStreamPath: def test_transcoded_priority(self): from app.services.audio import get_stream_path - from unittest.mock import MagicMock tmpdir = tempfile.mkdtemp() f1 = os.path.join(tmpdir, "t.ogg") f2 = os.path.join(tmpdir, "t.mp3") @@ -99,7 +94,6 @@ class TestGetStreamPath: def test_fallback_original(self): from app.services.audio import get_stream_path - from unittest.mock import MagicMock tmpdir = tempfile.mkdtemp() f = os.path.join(tmpdir, "t.mp3") open(f, "w").close() @@ -112,7 +106,6 @@ class TestGetStreamPath: def test_no_files(self): from app.services.audio import get_stream_path - from unittest.mock import MagicMock song = MagicMock() song.transcoded_path = None song.file_path = None diff --git a/PLAN.md b/docs/PLAN.md similarity index 100% rename from PLAN.md rename to docs/PLAN.md diff --git a/test-results/features-Internet-Radio-Page-shows-internet-radio-title-chromium/error-context.md b/test-results/features-Internet-Radio-Page-shows-internet-radio-title-chromium/error-context.md deleted file mode 100644 index 4a8ad1d..0000000 --- a/test-results/features-Internet-Radio-Page-shows-internet-radio-title-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Internet Radio Page >> shows internet radio title -- Location: e2e/features.spec.ts:74:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio -Call log: - - navigating to "http://localhost:5173/radio", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { -> 71 | await page.goto('/radio'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-Internet-Radio-Page-shows-search-bar-chromium/error-context.md b/test-results/features-Internet-Radio-Page-shows-search-bar-chromium/error-context.md deleted file mode 100644 index 2337c91..0000000 --- a/test-results/features-Internet-Radio-Page-shows-search-bar-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Internet Radio Page >> shows search bar -- Location: e2e/features.spec.ts:78:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio -Call log: - - navigating to "http://localhost:5173/radio", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { -> 71 | await page.goto('/radio'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-Internet-Radio-Page-shows-stations-section-chromium/error-context.md b/test-results/features-Internet-Radio-Page-shows-stations-section-chromium/error-context.md deleted file mode 100644 index bf86f6f..0000000 --- a/test-results/features-Internet-Radio-Page-shows-stations-section-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Internet Radio Page >> shows stations section -- Location: e2e/features.spec.ts:83:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio -Call log: - - navigating to "http://localhost:5173/radio", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { -> 71 | await page.goto('/radio'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-LoFi-Channel-Page-shows-LoFi-label-on-channels-chromium/error-context.md b/test-results/features-LoFi-Channel-Page-shows-LoFi-label-on-channels-chromium/error-context.md deleted file mode 100644 index 3a39d9e..0000000 --- a/test-results/features-LoFi-Channel-Page-shows-LoFi-label-on-channels-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> LoFi Channel Page >> shows LoFi label on channels -- Location: e2e/features.spec.ts:55:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi -Call log: - - navigating to "http://localhost:5173/lofi", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { -> 42 | await page.goto('/lofi'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-LoFi-Channel-Page-shows-lofi-channel-title-chromium/error-context.md b/test-results/features-LoFi-Channel-Page-shows-lofi-channel-title-chromium/error-context.md deleted file mode 100644 index 8a2661b..0000000 --- a/test-results/features-LoFi-Channel-Page-shows-lofi-channel-title-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> LoFi Channel Page >> shows lofi channel title -- Location: e2e/features.spec.ts:45:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi -Call log: - - navigating to "http://localhost:5173/lofi", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { -> 42 | await page.goto('/lofi'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-LoFi-Channel-Page-shows-lofi-channels-chromium/error-context.md b/test-results/features-LoFi-Channel-Page-shows-lofi-channels-chromium/error-context.md deleted file mode 100644 index 33309f4..0000000 --- a/test-results/features-LoFi-Channel-Page-shows-lofi-channels-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> LoFi Channel Page >> shows lofi channels -- Location: e2e/features.spec.ts:49:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi -Call log: - - navigating to "http://localhost:5173/lofi", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { -> 42 | await page.goto('/lofi'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-clicking-set-the-mood-changes-mood-chromium/error-context.md b/test-results/features-Mood-Radio-Page-clicking-set-the-mood-changes-mood-chromium/error-context.md deleted file mode 100644 index 8ca2ade..0000000 --- a/test-results/features-Mood-Radio-Page-clicking-set-the-mood-changes-mood-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> clicking set the mood changes mood -- Location: e2e/features.spec.ts:25:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-shows-circular-progress-indicator-chromium/error-context.md b/test-results/features-Mood-Radio-Page-shows-circular-progress-indicator-chromium/error-context.md deleted file mode 100644 index 5ab5345..0000000 --- a/test-results/features-Mood-Radio-Page-shows-circular-progress-indicator-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> shows circular progress indicator -- Location: e2e/features.spec.ts:33:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-shows-mood-name-chromium/error-context.md b/test-results/features-Mood-Radio-Page-shows-mood-name-chromium/error-context.md deleted file mode 100644 index ad9412e..0000000 --- a/test-results/features-Mood-Radio-Page-shows-mood-name-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> shows mood name -- Location: e2e/features.spec.ts:12:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-shows-mood-radio-title-chromium/error-context.md b/test-results/features-Mood-Radio-Page-shows-mood-radio-title-chromium/error-context.md deleted file mode 100644 index 313d4cd..0000000 --- a/test-results/features-Mood-Radio-Page-shows-mood-radio-title-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> shows mood radio title -- Location: e2e/features.spec.ts:8:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-shows-play-mood-button-chromium/error-context.md b/test-results/features-Mood-Radio-Page-shows-play-mood-button-chromium/error-context.md deleted file mode 100644 index bc0593c..0000000 --- a/test-results/features-Mood-Radio-Page-shows-play-mood-button-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> shows play mood button -- Location: e2e/features.spec.ts:17:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-shows-set-the-mood-button-chromium/error-context.md b/test-results/features-Mood-Radio-Page-shows-set-the-mood-button-chromium/error-context.md deleted file mode 100644 index a023ec0..0000000 --- a/test-results/features-Mood-Radio-Page-shows-set-the-mood-button-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> shows set the mood button -- Location: e2e/features.spec.ts:21:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-New-Releases-Page-shows-empty-state-or-releases-chromium/error-context.md b/test-results/features-New-Releases-Page-shows-empty-state-or-releases-chromium/error-context.md deleted file mode 100644 index 3feb8db..0000000 --- a/test-results/features-New-Releases-Page-shows-empty-state-or-releases-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> New Releases Page >> shows empty state or releases -- Location: e2e/features.spec.ts:97:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/releases -Call log: - - navigating to "http://localhost:5173/releases", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { -> 90 | await page.goto('/releases'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/releases - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-New-Releases-Page-shows-new-releases-title-chromium/error-context.md b/test-results/features-New-Releases-Page-shows-new-releases-title-chromium/error-context.md deleted file mode 100644 index 3bf8d38..0000000 --- a/test-results/features-New-Releases-Page-shows-new-releases-title-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> New Releases Page >> shows new releases title -- Location: e2e/features.spec.ts:93:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/releases -Call log: - - navigating to "http://localhost:5173/releases", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { -> 90 | await page.goto('/releases'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/releases - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-SharePlay-Page-shows-create-room-option-chromium/error-context.md b/test-results/features-SharePlay-Page-shows-create-room-option-chromium/error-context.md deleted file mode 100644 index 8939dc4..0000000 --- a/test-results/features-SharePlay-Page-shows-create-room-option-chromium/error-context.md +++ /dev/null @@ -1,150 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> SharePlay Page >> shows create room option -- Location: e2e/features.spec.ts:113:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/shareplay -Call log: - - navigating to "http://localhost:5173/shareplay", waiting until "load" - -``` - -# Test source - -```ts - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -> 106 | await page.goto('/shareplay'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/shareplay - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-SharePlay-Page-shows-shareplay-title-chromium/error-context.md b/test-results/features-SharePlay-Page-shows-shareplay-title-chromium/error-context.md deleted file mode 100644 index ce70607..0000000 --- a/test-results/features-SharePlay-Page-shows-shareplay-title-chromium/error-context.md +++ /dev/null @@ -1,150 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> SharePlay Page >> shows shareplay title -- Location: e2e/features.spec.ts:109:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/shareplay -Call log: - - navigating to "http://localhost:5173/shareplay", waiting until "load" - -``` - -# Test source - -```ts - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -> 106 | await page.goto('/shareplay'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/shareplay - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file