🏡 index : quine-global/quintodrome.git

import React from 'react'
import { render, fireEvent, screen } from '@testing-library/react'
import { TestContext } from 'ra-test'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { ThemeProvider, createTheme } from '@material-ui/core/styles'
import { AlbumContextMenu, ArtistContextMenu } from './ContextMenus'

const mockDispatch = vi.fn()
vi.mock('react-redux', () => ({ useDispatch: () => mockDispatch }))

const { mockConfig } = vi.hoisted(() => ({
  mockConfig: {
    enableSharing: true,
    enableDownloads: true,
    enableFavourites: false,
  },
}))
vi.mock('../config', () => ({ default: mockConfig }))

const { mockPermissions, mockRefreshMetadata } = vi.hoisted(() => ({
  mockPermissions: { value: 'admin' },
  mockRefreshMetadata: vi.fn(),
}))

vi.mock('react-admin', async (importOriginal) => {
  const actual = await importOriginal()
  return {
    ...actual,
    useNotify: () => vi.fn(),
    usePermissions: () => ({ permissions: mockPermissions.value }),
    useDataProvider: () => ({
      getList: vi.fn(),
      refreshMetadata: mockRefreshMetadata,
    }),
    useTranslate: () => (x) => x,
  }
})

describe('ContextMenus', () => {
  const renderMenu = (Menu, record) => {
    render(
      <TestContext>
        <ThemeProvider theme={createTheme()}>
          <Menu record={record} />
        </ThemeProvider>
      </TestContext>,
    )
    fireEvent.click(screen.getByLabelText('more'))
  }

  beforeEach(() => {
    vi.clearAllMocks()
    mockConfig.enableSharing = true
    mockConfig.enableDownloads = true
    mockPermissions.value = 'admin'
  })

  describe('ArtistContextMenu', () => {
    const withAlbumArtist = {
      id: 'ar1',
      name: 'Artist',
      stats: { albumartist: { songCount: 3, albumCount: 1, size: 1024 } },
    }

    it('shows the album-artist size on the download item', () => {
      renderMenu(ArtistContextMenu, withAlbumArtist)
      expect(screen.getByText('ra.action.download (1 KB)')).toBeInTheDocument()
    })

    it('hides download and share for artists with no album-artist content', () => {
      renderMenu(ArtistContextMenu, { id: 'ar1', name: 'Artist', stats: {} })
      expect(screen.queryByText(/ra\.action\.download/)).not.toBeInTheDocument()
      expect(screen.queryByText('ra.action.share')).not.toBeInTheDocument()
    })
  })

  describe('AlbumContextMenu', () => {
    it('uses the total size on the album download item', () => {
      renderMenu(AlbumContextMenu, {
        id: 'al1',
        name: 'Album',
        duration: 100,
        size: 1024 * 1024,
      })
      expect(screen.getByText('ra.action.download (1 MB)')).toBeInTheDocument()
    })
  })

  describe('refresh metadata', () => {
    it('shows the item for admins on the album menu', () => {
      renderMenu(AlbumContextMenu, { id: 'al1', name: 'Album', songCount: 1 })
      expect(
        screen.getByText('resources.album.actions.refresh'),
      ).toBeInTheDocument()
    })

    // Menu order comes from key insertion order in the options object, so it is easy to
    // change by accident when adding an entry.
    it('places the item directly above Get Info', () => {
      renderMenu(AlbumContextMenu, { id: 'al1', name: 'Album', songCount: 1 })
      const labels = screen
        .getAllByRole('menuitem')
        .map((item) => item.textContent)
      const refreshAt = labels.indexOf('resources.album.actions.refresh')
      const infoAt = labels.indexOf('resources.album.actions.info')

      expect(refreshAt).toBeGreaterThanOrEqual(0)
      expect(infoAt).toEqual(refreshAt + 1)
    })

    it('shows the item for admins on the artist menu', () => {
      renderMenu(ArtistContextMenu, { id: 'ar1', name: 'Artist', stats: {} })
      expect(
        screen.getByText('resources.album.actions.refresh'),
      ).toBeInTheDocument()
    })

    it('hides the item for regular users', () => {
      mockPermissions.value = 'regular'
      renderMenu(AlbumContextMenu, { id: 'al1', name: 'Album', songCount: 1 })
      expect(
        screen.queryByText('resources.album.actions.refresh'),
      ).not.toBeInTheDocument()
    })

    it('calls refreshMetadata with the resource and id', () => {
      mockRefreshMetadata.mockResolvedValue({})
      renderMenu(AlbumContextMenu, { id: 'al1', name: 'Album', songCount: 1 })
      fireEvent.click(screen.getByText('resources.album.actions.refresh'))
      expect(mockRefreshMetadata).toHaveBeenCalledWith('album', 'al1')
    })
  })
})