Skip to content
Merged
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
50 changes: 24 additions & 26 deletions src/App.auth.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ describe('App auth', () => {
])
})

it('opens the server-backed artists workspace without full catalog hydration', async () => {
it('opens editable artist records after login with full catalog hydration', async () => {
h.clearCatalogForTests()
h.clearAuthSessionForTests()
window.history.pushState({}, '', '/artists')
Expand All @@ -208,8 +208,7 @@ describe('App auth', () => {
email: 'collector@cratebase.local',
roles: ['Admin'],
}),
h.searchResponseWithArtist(),
h.graphResponseForArtist(),
...h.emptyCatalogLoadResponses(),
)
const user = h.userEvent.setup()
h.render(<h.App />)
Expand All @@ -223,25 +222,16 @@ describe('App auth', () => {
await user.click(h.within(form).getByRole('button', { name: 'Sign in' }))

expect(
await h.screen.findByRole('row', { name: /New Order/i }),
await h.screen.findByRole('heading', { name: 'Artist index' }),
).toBeInTheDocument()
expect(
await h.screen.findByRole('heading', { name: 'New Order' }),
h.screen.getByRole('searchbox', { name: 'Search artists' }),
).toBeInTheDocument()
expect(
fetchMock.mock.calls.map(([input]) =>
typeof input === 'string'
? input
: input instanceof URL
? input.toString()
: input.url,
),
).toEqual([
'/api/auth/session',
'/api/auth/login',
'/api/search?entityType=artist&limit=100&offset=0',
'/api/catalog-graph/artist/artist-1',
])

const urls = requestUrls(fetchMock)
expect(urls.slice(0, 2)).toEqual(['/api/auth/session', '/api/auth/login'])
expect(urls).toContain('/api/artists?limit=100&offset=0')
expect(urls.some((url) => url.startsWith('/api/search?'))).toBe(false)
})

it('shows an accessible error after invalid login', async () => {
Expand Down Expand Up @@ -334,14 +324,15 @@ describe('App auth', () => {
).toBeEnabled()
})

it('shows a server-backed workspace API error without full catalog fallback', async () => {
it('shows a full catalog load error for editable workspaces', async () => {
h.clearCatalogForTests()
window.history.pushState({}, '', '/tracks')
const fetchMock = h.mockFetch(
h.jsonResponse(
{ code: 'catalog.server_error', message: 'Catalog unavailable' },
500,
),
...h.emptyCatalogLoadResponses().slice(1),
)

h.render(<h.App />)
Expand All @@ -350,18 +341,16 @@ describe('App auth', () => {
await h.screen.findByRole('heading', { name: 'Tracks' }),
).toBeInTheDocument()
expect(await h.screen.findByRole('alert')).toHaveTextContent(
'Catalog unavailable',
'Catalog request failed. Try again.',
)
expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([
'/api/search?entityType=track&limit=100&offset=0',
])
expect(requestUrls(fetchMock)).toContain('/api/artists?limit=100&offset=0')
})

it('returns to sign in when a catalog mutation expires the session', async () => {
h.clearCatalogForTests()
window.history.pushState({}, '', '/artists')
h.mockFetch(
h.emptySearchResponse(),
...h.emptyCatalogLoadResponses(),
h.jsonResponse(
{ code: 'auth.unauthenticated', message: 'Session expired' },
401,
Expand All @@ -387,7 +376,6 @@ describe('App auth', () => {
h.clearCatalogForTests()
window.history.pushState({}, '', '/labels')
h.mockFetch(
h.emptySearchResponse(),
...h.emptyCatalogLoadResponses(),
h.jsonResponse({
id: '00000000-0000-7000-8000-000000000010',
Expand Down Expand Up @@ -473,3 +461,13 @@ describe('App auth', () => {
)
})
})

function requestUrls(fetchMock: ReturnType<typeof h.mockFetch>) {
return fetchMock.mock.calls.map(([input]) =>
typeof input === 'string'
? input
: input instanceof URL
? input.toString()
: input.url,
)
}
5 changes: 2 additions & 3 deletions src/App.catalog-actions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,7 @@ describe('App catalog actions', () => {
h.mockFetch(
h.searchResponseWithLabel(),
h.graphResponseForLabel(),
h.searchResponseWithLabel(),
h.graphResponseForLabel(),
...h.catalogLoadResponsesWithLabels(),
)
const user = h.userEvent.setup()

Expand All @@ -168,7 +167,7 @@ describe('App catalog actions', () => {
'page',
)
expect(
h.screen.getByRole('heading', { name: 'Factory Records' }),
h.screen.getByRole('complementary', { name: 'Factory Records' }),
).toBeInTheDocument()
})

Expand Down
57 changes: 57 additions & 0 deletions src/App.catalog-server-detail.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { describe, expect, it } from 'vitest'
import * as h from './test/appTestHarness'
import {
graphResponseForReleaseWithDuplicateArtists,
releaseDetailWithoutCover,
searchResponseWithRelease,
} from './test/catalogActionFixtures'

h.setupAppTestHooks()

describe('App catalog server detail panel', () => {
it('renders release details with readable roles, merged artists, and cover actions', async () => {
window.history.pushState({}, '', '/catalog')
h.clearCatalogForTests()
h.mockFetch(
searchResponseWithRelease(),
graphResponseForReleaseWithDuplicateArtists(),
releaseDetailWithoutCover(),
)

h.render(<h.App />)

const detailPanel = await h.screen.findByRole('complementary', {
name: 'Stripped',
})
const artistsSection = h.detailSection(detailPanel, 'Artists')

expect(
h.within(artistsSection).getByRole('link', { name: 'Depeche Mode' }),
).toHaveAttribute('href', '/artists?artist=artist-depeche-mode')
expect(
h.within(artistsSection).getByText('Main artist'),
).toBeInTheDocument()
expect(
h.within(artistsSection).getAllByRole('link', {
name: 'Depeche Mode',
}),
).toHaveLength(1)
expect(
h.within(detailPanel).queryByRole('heading', { name: 'Credits' }),
).not.toBeInTheDocument()
expect(
h.within(detailPanel).queryByRole('heading', { name: 'TRACKLIST' }),
).not.toBeInTheDocument()
expect(
h.within(detailPanel).queryByRole('heading', { name: 'LABEL' }),
).not.toBeInTheDocument()
expect(
h.within(detailPanel).getByLabelText('Upload cover'),
).toBeInTheDocument()
expect(
h.within(detailPanel).queryByRole('button', {
name: 'Load editable view',
}),
).not.toBeInTheDocument()
})
})
Loading