Two Electron E2E regressions from this branch's redesigns:
1. Hiding the playlist switcher on /settings (a playlist-scoped control)
also hid the global "Add playlist" button, which lives in the same
header-actions block. A user configuring Settings before importing
their first source had no way to add a playlist — and the settings /
playlist-switcher E2E suites, which add a portal right after saving a
setting, timed out waiting for the button. The Add Playlist button is
a global action, so it now stays visible on Settings; only the
per-playlist context shortcut and bulk actions are hidden there.
2. dashboard-activation.e2e.ts asserted on `dashboard-global-favorites-
rail`, the mixed Favorites rail removed earlier in this branch. The
test now targets the v0.22 rails: live favorites resolve through the
favorites-first `dashboard-live-recent-rail`, and the played movie /
series resolve through `dashboard-continue-watching-rail` (renamed
from `dashboard-recently-watched-rail`). The movie-from-favorites
assertion was dropped — that dashboard surface no longer exists; the
favorites collection still has its own page-level coverage.
Verified locally: dashboard-activation, settings, and playlist-switcher
E2E specs all pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The redesign rollup in this branch replaced the Add Playlist dialog's
2-level type × subtype tabs with a single flat 5-card radiogroup, and
replaced the Settings theme list with a compact segmented control whose
options are just "Light"/"Dark"/"System" (no "theme" suffix). E2E tests
that pinned to the old roles/labels failed in CI:
- basic / xtream / stalker / self-hosted: getByRole('tab', ...) on
"Add via file upload" / "Xtream" / "Stalker" — there are no tabs in
the new dialog. Switched to getByRole('radio', { name: /M3U file/i })
and friends, matched against the new radio labels.
- settings: getByRole('radio', { name: 'System theme' }) — the new
picker uses just "System". Scoped to the [data-test-id="select-theme"]
radiogroup so it doesn't collide with the identically-labelled
cover-size options below.
The Electron `clickDialogSegmentedOption` helper grew a radio-role lookup
as its primary path and keeps the old tab/button/legacy-selector
fallbacks so a future redesign won't break every fixture again. The
"M3U" parent category becomes a no-op (the new picker has no standalone
M3U tile — callers always specialise to M3U URL/file/text immediately
after, which is what we want anyway).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Simplified expect statements for channel items and content cards in favorites and recent tests.
- Added new tests for opening Xtream favorite movies and series inline from favorites without switching playlists.
- Enhanced existing tests to ensure consistent behavior when navigating back from detail views.
- Introduced utility function expectInlineCollectionDetail to reduce code duplication in inline detail checks.
- Improved error handling for back button clicks in detail views.
Entire-Checkpoint: c6e522b4276c
Two separate root causes:
1. category-management.e2e.ts: openWorkspaceSection('Live TV') was not
awaited — pickSidebarCategory ran before the route settled on the live
section, so it read VOD categories from the stale sidebar (e.g. 'Action'
instead of a live category). The manage-categories dialog opens for the
live section, so searching for a VOD name found nothing.
Fix: add waitForURL(/\/workspace\/xtreams\/[^/]+\/live/) after both
openWorkspaceSection('Live TV') calls.
2. recent.e2e.ts / favorites.e2e.ts: waitForFirstGridListCardTitle read
the first card's title, but clickGridListCardByTitle then did a fresh
DOM search by that title. Between those two calls the grid could
re-sort/paginate, making the card unfindable.
Fix: new clickFirstGridListCard helper atomically reads the title and
clicks the same card reference, replacing the two-step pattern.
https://claude.ai/code/session_01VU6ZZTG7YrhKhKBQvBfmpp
The dialog's 'Select All' button appears as soon as isLoading() flips,
but Angular still needs a change-detection cycle to paint the @for loop.
On slower CI runners (especially after a refreshSource cycle) the
.category-item rows were not yet in the DOM when toggleManagedCategory
tried to locate them, causing a spurious 5 s timeout.
Fix: add an explicit `toBeVisible` guard on the first .category-item
inside openManageCategoriesDialog, and raise the per-row timeout in
toggleManagedCategory from 5 s to 15 s.
https://claude.ai/code/session_01VU6ZZTG7YrhKhKBQvBfmpp
- category-management: replace filter({ has: dialog.locator(...) }) with
filter({ hasText }) on the .category-item row. Playwright cannot scope an
absolute locator (created via dialog.locator()) to each candidate element
when used inside filter({ has: }), so the query always returned empty.
Using hasText on the whole row correctly retries until Angular's @for loop
re-renders filteredCategories() after the search field is populated.
- recent.e2e.ts Xtream test: same pagination fix as favorites — derive
movieTitle and seriesTitle from waitForFirstGridListCardTitle() instead of
pickDistinctTitles(fixture.items)[0], which may be on page 2 of the
date-sorted grid and never visible.
https://claude.ai/code/session_01VU6ZZTG7YrhKhKBQvBfmpp
- toggleManagedCategory: replace manual count-loop with Playwright filter so
the locator retries internally until Angular re-renders the filtered list
after the search field is populated (previously called .count() before the
@for loop had reacted to the updated searchTerm signal)
- favorites Xtream test: pick movie/series titles from the first displayed
grid card instead of vodFixture.items[0]. The grid sorts by date-desc and
paginates at 25 items; the first API item may land on page 2 making
clickGridListCardByTitle time out with "element not found". Reading the
title from the DOM after navigation ensures we always target a visible card.
- electron-test-fixtures: export waitForFirstGridListCardTitle helper
https://claude.ai/code/session_01VU6ZZTG7YrhKhKBQvBfmpp
- dragSourceBefore: replace mat-list-item with .playlist-item as the
drag target; playlist-item.component has no mat-list-item element,
the inner container div carries the .playlist-item class
- toggleManagedCategory: match categories by name only instead of
name+itemCount; after an Xtream refresh the itemCounts Map passed to
the dialog may be empty (streams not yet counted), causing a false
mismatch even though the category is present and uniquely named
https://claude.ai/code/session_01VU6ZZTG7YrhKhKBQvBfmpp
- Fix getVisibleSourceTitles: use .playlist-title selector instead of
.mdc-list-item__primary-text which no longer matches playlist-item component
- Fix category-management test: re-navigate to sources page after
refreshSource since refresh navigates away from sources
- Fix recent.e2e test: remove switchUnifiedCollectionContent calls after
clearRecentItems since the content toggle disappears when all items cleared
- Fix stalker mock server: generate numeric episode IDs ("1","2",...) in
season.series array so toSeriesNumbers() can parse them; previously used
non-numeric IDs like "30000-s1-e1" which parsed to NaN, producing empty
episode arrays and causing playFirstSeriesEpisode to time out
https://claude.ai/code/session_01VU6ZZTG7YrhKhKBQvBfmpp
Replace menuitem selectors with mat-button-toggle[value] selectors in
all four playlist import helpers (file, url, xtream, stalker) and rename
openAddPlaylistMenu to openAddPlaylistDialog to reflect that clicking
"Add playlist" now opens a dialog directly instead of a dropdown menu.
https://claude.ai/code/session_01VU6ZZTG7YrhKhKBQvBfmpp
- Implemented tests for tracking recently viewed channels in M3U playlists, ensuring they persist across app restarts.
- Added functionality to manage Xtream and Stalker history, including support for clearing recent items.
- Enhanced settings tests to verify persistence of user preferences across app restarts, including language, video player, and theme settings.
- Created comprehensive tests for managing sources, including filtering, sorting, editing, and deleting M3U, Xtream, and Stalker sources.
- Introduced mock fixtures for Xtream and Stalker categories to facilitate testing.
- Added DatabaseWorkerClient to manage database operations in a worker thread.
- Created database-worker-client.ts to handle requests and responses between the main process and the worker.
- Introduced database-worker.types.ts for type definitions related to worker messages and operations.
- Implemented database.worker.ts to execute various database operations, including CRUD operations for playlists, content, and favorites.
- Added database.worker-connection.ts to manage database connections and ensure proper handling of SQLite instances.
- Created tests for DatabaseWorkerClient to ensure correct request handling and error management.
- Updated xtream.events.ts to allow suppression of error logging for specific requests.
- Enhanced EPG parser worker to include a busy timeout for SQLite operations.
- Integrated shutdown logic for the DatabaseWorkerClient in the main application lifecycle.
- Added `GlobalFavoritesListComponent` to display and manage favorite channels.
- Integrated drag-and-drop functionality for reordering favorite channels.
- Created HTML and SCSS files for the global favorites list layout and styling.
- Implemented logic to handle empty states and search functionality within the favorites list.
feat: Enhance unified collection page with live and grid tabs
- Developed `UnifiedCollectionPageComponent` to manage different content types (live, movies, series).
- Added `UnifiedGridTabComponent` and `UnifiedLiveTabComponent` for displaying content in grid and live formats.
- Implemented loading states and empty states for better user experience.
- Integrated EPG (Electronic Program Guide) functionality for live channels.
style: Refactor styles for unified collection components
- Updated SCSS styles for `UnifiedGridTabComponent` and `UnifiedLiveTabComponent` to improve layout and responsiveness.
- Added skeleton loading states for better visual feedback during data fetching.
fix: Improve scrolling behavior in category view component
- Adjusted scrolling logic in `CategoryViewComponent` to ensure selected categories are centered in the view.
Entire-Checkpoint: 0e4488af6c76