Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 11 additions & 21 deletions src/__tests__/library.test-e2e.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { expect, test } from 'vite-plus/test';
import { page, userEvent } from 'vite-plus/test/browser';

import { beforeEachSetup } from './test-helpers';
import { beforeEachSetup, goToLibrary, scanLibrary } from './test-helpers';

beforeEachSetup();

test('The library tab should display all tracks', async () => {
// Fake the import of tracks
await page.getByTestId('footer-settings-link').click();
await page.getByTestId('scan-library-button').click();
await page.getByTestId('footer-library-link').click();
await scanLibrary();
await goToLibrary();

// Ensure we have the 3 test-tracks, but no more
await expect.element(page.getByTestId('track-row-0')).toBeInTheDocument();
Expand All @@ -29,10 +27,8 @@ test('The library tab should display all tracks', async () => {
});

test('Tracks should selectable via click + modifiers', async () => {
// Fake the import of tracks
await page.getByTestId('footer-settings-link').click();
await page.getByTestId('scan-library-button').click();
await page.getByTestId('footer-library-link').click();
await scanLibrary();
await goToLibrary();

const firstTrack = page.getByTestId(/track-row-/).first();
const secondTrack = page.getByTestId(/track-row-/).nth(1);
Expand Down Expand Up @@ -72,10 +68,8 @@ test('Tracks should selectable via click + modifiers', async () => {
});

test('Tracks should be selectable via keyboard only (after a single selection)', async () => {
// Fake the import of tracks
await page.getByTestId('footer-settings-link').click();
await page.getByTestId('scan-library-button').click();
await page.getByTestId('footer-library-link').click();
await scanLibrary();
await goToLibrary();

const firstTrack = page.getByTestId(/track-row-/).first();
const secondTrack = page.getByTestId(/track-row-/).nth(1);
Expand Down Expand Up @@ -131,10 +125,8 @@ test('Tracks should be selectable via keyboard only (after a single selection)',
});

test('Search should filter tracks in the library', async () => {
// Fake the import of tracks
await page.getByTestId('footer-settings-link').click();
await page.getByTestId('scan-library-button').click();
await page.getByTestId('footer-library-link').click();
await scanLibrary();
await goToLibrary();

const search = page.getByTestId('library-search');
const searchClear = page.getByTestId('library-search-clear');
Expand Down Expand Up @@ -174,10 +166,8 @@ test('Search should filter tracks in the library', async () => {
});

test('Column headers should sort tracks in the library', async () => {
// Fake the import of tracks
await page.getByTestId('footer-settings-link').click();
await page.getByTestId('scan-library-button').click();
await page.getByTestId('footer-library-link').click();
await scanLibrary();
await goToLibrary();

const firstTrack = page.getByTestId(/track-row-/).first();
const secondTrack = page.getByTestId(/track-row-/).nth(1);
Expand Down
8 changes: 3 additions & 5 deletions src/__tests__/player.test-e2e.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect, test } from 'vite-plus/test';
import { page } from 'vite-plus/test/browser';

import { beforeEachSetup } from './test-helpers';
import { beforeEachSetup, goToLibrary, scanLibrary } from './test-helpers';

beforeEachSetup();

Expand All @@ -14,10 +14,8 @@ test('Double click on a track should play it and display its metadata', async ()
.element(page.getByTestId('playercontrol-pause'))
.not.toBeInTheDocument();

// Fake the import of tracks
await page.getByTestId('footer-settings-link').click();
await page.getByTestId('scan-library-button').click();
await page.getByTestId('footer-library-link').click();
await scanLibrary();
await goToLibrary();

// Double-clicking on a track should start the player
await page.getByTestId('track-row-0').dblClick();
Expand Down
77 changes: 77 additions & 0 deletions src/__tests__/playlists.test-e2e.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { expect, test } from 'vite-plus/test';
import { page, userEvent } from 'vite-plus/test/browser';

import { beforeEachSetup, goToPlaylists } from './test-helpers';

beforeEachSetup();

async function createPlaylist() {
await page.getByTestId('playlist-new-button').click();
}

async function renamePlaylist(currentName: string, newName: string) {
await page.getByRole('link', { name: currentName }).dblClick();
const input = page.getByTestId('playlist-rename-input');
await input.clear();
await input.fill(newName);
await userEvent.keyboard('[Enter]');
}

test('Playlists', async () => {
await goToPlaylists();

// Empty state when no playlists exist
await expect
.element(page.getByTestId('view-message'))
.toHaveTextContent("You haven't created any playlist yet");

// Clicking "create one now" creates a playlist and shows the empty playlist view
await page.getByTestId('create-playlist-call-to-action').click();
await expect
.element(page.getByTestId('view-message'))
.toHaveTextContent('Empty playlist');

// Creating more playlists via the + button shows them all in the sidebar
await createPlaylist();
await createPlaylist();
const playlistLinks = page.getByRole('link', { name: 'New playlist' });
await expect.element(playlistLinks.nth(0)).toBeInTheDocument();
await expect.element(playlistLinks.nth(1)).toBeInTheDocument();
await expect.element(playlistLinks.nth(2)).toBeInTheDocument();
await expect.element(playlistLinks.nth(3)).not.toBeInTheDocument();

// Rename via Escape cancels the rename
await page.getByRole('link', { name: 'New playlist' }).first().dblClick();
const input = page.getByTestId('playlist-rename-input');
await input.clear();
await input.fill('Cancelled Name');
await userEvent.keyboard('[Escape]');
await expect
.element(page.getByRole('link', { name: 'Cancelled Name' }))
.not.toBeInTheDocument();

// Rename via Enter commits the new name
await renamePlaylist('New playlist', 'Alpha');
await expect
.element(page.getByRole('link', { name: 'Alpha' }))
.toBeInTheDocument();

await renamePlaylist('New playlist', 'Another Blues');
await renamePlaylist('New playlist', 'Best Of');

// Playlists are grouped by first letter
const letterGroups = page.getByTestId('sidenav-letter-group');
await expect.element(letterGroups.nth(0)).toHaveTextContent('A');
await expect.element(letterGroups.nth(1)).toHaveTextContent('B');
await expect.element(letterGroups.nth(2)).not.toBeInTheDocument();

await expect
.element(page.getByRole('link', { name: 'Alpha' }))
.toBeInTheDocument();
await expect
.element(page.getByRole('link', { name: 'Another Blues' }))
.toBeInTheDocument();
await expect
.element(page.getByRole('link', { name: 'Best Of' }))
.toBeInTheDocument();
});
28 changes: 28 additions & 0 deletions src/__tests__/test-helpers.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
import { i18n } from '@lingui/core';
import { beforeEach, vi } from 'vite-plus/test';
import { page } from 'vite-plus/test/browser';

// ---------------------------------------------------------------------------
// Navigation helpers
// ---------------------------------------------------------------------------

export async function goToLibrary() {
await page.getByTestId('footer-library-link').click();
}

export async function goToPlaylists() {
await page.getByTestId('footer-playlists-link').click();
}

export async function goToSettings() {
await page.getByTestId('footer-settings-link').click();
}

// ---------------------------------------------------------------------------
// Library helpers
// ---------------------------------------------------------------------------

/** Triggers a library scan using the mock tracks */
export async function scanLibrary() {
await goToSettings();
await page.getByTestId('scan-library-button').click();
}

import { render } from 'vitest-browser-react';

import { MOCK_CONFIG } from '../lib/__mocks__/bridge-config.ts';
Expand Down
7 changes: 6 additions & 1 deletion src/components/SideNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ export default function SideNav(props: Props) {
{Object.entries(groupedChildren).map(([letter, children]) => {
return (
<React.Fragment key={letter}>
<div {...stylex.props(styles.letter)}>{letter}</div>
<div
{...stylex.props(styles.letter)}
data-testid="sidenav-letter-group"
>
{letter}
</div>
<div {...stylex.props(styles.items)}>{children}</div>
</React.Fragment>
);
Expand Down
12 changes: 12 additions & 0 deletions src/components/SideNavLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,30 @@ export default function SideNavLink(props: Props): React.ReactNode {
onBlur={onBlur}
onFocus={onFocus}
ref={(ref) => ref?.focus()}
data-testid="playlist-rename-input"
{...stylex.props(styles.sideNavLink, styles.sideNavLinkInput)}
/>
);
}

const onDoubleClick = useCallback(
(e: React.MouseEvent) => {
if (onRename) {
e.preventDefault();
setRenamed(true);
}
},
[onRename],
);

return (
<NavigationMenu.Item>
<NavigationMenu.Link
render={(renderProps) => (
<Link
{...renderProps}
onContextMenu={onContextMenu}
onDoubleClick={onDoubleClick}
draggable={false}
{...linkOptions}
{...stylex.props(styles.sideNavLink)}
Expand Down
13 changes: 12 additions & 1 deletion src/elements/ButtonIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,27 @@ type Props = React.ComponentPropsWithRef<'button'> & {
iconSize?: IconSize;
isActive?: boolean;
xstyle?: stylex.CompiledStyles;
'data-testid'?: string;
};

export default function ButtonIcon(props: Props) {
const { onClick, icon, iconSize, isActive, ref, xstyle, ...rest } = props;
const {
onClick,
icon,
iconSize,
isActive,
ref,
xstyle,
'data-testid': testId,
...rest
} = props;
return (
<button
ref={ref}
type="button"
onClick={onClick}
data-museeks-action
data-testid={testId}
{...rest}
{...stylex.props(styles.button, xstyle)}
>
Expand Down
19 changes: 12 additions & 7 deletions src/elements/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ interface LinkProps<
children: React.ReactNode;
inheritColor?: boolean;
type?: keyof typeof typeVariants;
'data-testid'?: string;
}

interface ButtonProps {
onClick: () => void;
children: React.ReactNode;
inheritColor?: boolean;
type?: keyof typeof typeVariants;
'data-testid'?: string;
}

/**
Expand All @@ -31,22 +33,25 @@ export default function Link<TRouter extends RegisteredRouter, TOptions>(
export default function Link(props: LinkProps | ButtonProps): React.ReactNode {
const type = props.type ?? 'bold';

const appliedStyles = stylex.props(
styles.link,
props.inheritColor === true && styles.inheritColor,
typeVariants[type],
);
const commonProps = {
dataTestID: props['data-testid'],
...stylex.props(
styles.link,
props.inheritColor === true && styles.inheritColor,
typeVariants[type],
),
};

if ('linkOptions' in props) {
return (
<RouterLink draggable={false} {...props.linkOptions} {...appliedStyles}>
<RouterLink draggable={false} {...props.linkOptions} {...commonProps}>
{props.children}
</RouterLink>
);
}

return (
<button onClick={props.onClick} type="button" {...appliedStyles}>
<button onClick={props.onClick} type="button" {...commonProps}>
{props.children}
</button>
);
Expand Down
Loading
Loading