Merge pull request 'chore: remove test artifacts, fix hardcoded paths, ruff clean, license' (#16) from improve/v1
This commit is contained in:
commit
64e4ca567d
4
.gitignore
vendored
4
.gitignore
vendored
@ -21,4 +21,6 @@ api-key.json
|
||||
music/*
|
||||
uploads/*
|
||||
static/*
|
||||
backend/app.db
|
||||
backend/app.dbcoverage/
|
||||
test-results/
|
||||
.coverage
|
||||
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal 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.
|
||||
45
README.md
45
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).
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
from .events import ConcertEvent
|
||||
|
||||
__all__ = [
|
||||
"Song",
|
||||
"Playlist",
|
||||
"PlaylistSong",
|
||||
"MoodCategory",
|
||||
"MoodSong",
|
||||
"LyricsCache",
|
||||
"LofiChannel",
|
||||
"RadioStation",
|
||||
"SharePlayRoom",
|
||||
"SharePlayCue",
|
||||
"UserSetting",
|
||||
"NewReleaseCheck",
|
||||
"ConcertEvent",
|
||||
]
|
||||
@ -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):
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi import APIRouter
|
||||
from typing import List, Optional
|
||||
from ..schemas.events import ConcertEventResponse
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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]
|
||||
|
||||
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
return {"message": "Server removed"}
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
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",
|
||||
]
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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}")
|
||||
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
"""Router tests - LoFi endpoints."""
|
||||
from app.models.lofi import LofiChannel
|
||||
from app.db.database import SessionLocal
|
||||
|
||||
|
||||
class TestListChannels:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
|
||||
@ -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())
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 | });
|
||||
```
|
||||
@ -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 | });
|
||||
```
|
||||
@ -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 | });
|
||||
```
|
||||
@ -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 | });
|
||||
```
|
||||
@ -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 | });
|
||||
```
|
||||
@ -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 | });
|
||||
```
|
||||
@ -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 }) => {
|
||||
```
|
||||
@ -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 }) => {
|
||||
```
|
||||
@ -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 }) => {
|
||||
```
|
||||
@ -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 }) => {
|
||||
```
|
||||
@ -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 }) => {
|
||||
```
|
||||
@ -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 }) => {
|
||||
```
|
||||
@ -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 | });
|
||||
```
|
||||
@ -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 | });
|
||||
```
|
||||
@ -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 | });
|
||||
```
|
||||
@ -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 | });
|
||||
```
|
||||
Loading…
x
Reference in New Issue
Block a user