chore: remove test artifacts, fix hardcoded paths, ruff clean, license

- Remove committed .coverage and test-results/ (196K screenshots); gitignore them
- Fix hardcoded  /home/userpath + venv/bin/python in test_endpoints.py
  (relative backend dir + sys.executable)
- Fix concatenated 'import json' in settings.py; bare excepts -> Exception;
  SQLAlchemy-safe is_active.is_(True); __all__ on models/schemas barrels
- ruff clean (93 fixes), MIT LICENSE, PLAN.md -> docs/, README Tests section
- 300 tests pass, 93.7% coverage
This commit is contained in:
Jarian Cottingham 2026-08-20 21:34:15 +00:00
parent 85d68a9313
commit 5c283eceef
45 changed files with 141 additions and 2376 deletions

BIN
.coverage

Binary file not shown.

4
.gitignore vendored
View File

@ -21,4 +21,6 @@ api-key.json
music/*
uploads/*
static/*
backend/app.db
backend/app.dbcoverage/
test-results/
.coverage

21
LICENSE Normal file
View File

@ -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.

View File

@ -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).

View File

@ -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:

View File

@ -7,3 +7,19 @@ from .shareplay import SharePlayRoom, SharePlayCue
from .settings import UserSetting
from .releases import NewReleaseCheck
from .events import ConcertEvent
__all__ = [
"Song",
"Playlist",
"PlaylistSong",
"MoodCategory",
"MoodSong",
"LyricsCache",
"LofiChannel",
"RadioStation",
"SharePlayRoom",
"SharePlayCue",
"UserSetting",
"NewReleaseCheck",
"ConcertEvent",
]

View File

@ -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):

View File

@ -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

View File

@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, Query
from fastapi import APIRouter
from typing import List, Optional
from ..schemas.events import ConcertEventResponse

View File

@ -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

View File

@ -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]

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,4 @@
import json
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List, Optional
@ -98,6 +99,3 @@ def remove_server(server_id: str, db: Session = Depends(get_db)):
db.commit()
return {"message": "Server removed"}
import json

View File

@ -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:

View File

@ -10,3 +10,31 @@ from .releases import NewReleaseResponse, ReleaseAlbumResponse
from .events import ConcertEventResponse
from .search import SearchResultResponse
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",
]

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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}")

View File

@ -1,6 +1,4 @@
"""Router tests - LoFi endpoints."""
from app.models.lofi import LofiChannel
from app.db.database import SessionLocal
class TestListChannels:

View File

@ -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:

View File

@ -2,7 +2,6 @@
import pytest
import sys
import os
import json
import tempfile
import shutil

View File

@ -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())

View File

@ -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

View File

@ -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 | });
```

View File

@ -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 | });
```

View File

@ -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 | });
```

View File

@ -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 | });
```

View File

@ -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 | });
```

View File

@ -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 | });
```

View File

@ -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 }) => {
```

View File

@ -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 }) => {
```

View File

@ -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 }) => {
```

View File

@ -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 }) => {
```

View File

@ -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 }) => {
```

View File

@ -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 }) => {
```

View File

@ -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 | });
```

View File

@ -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 | });
```

View File

@ -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 | });
```

View File

@ -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 | });
```