nukeop/nuclear

View on GitHub
packages/app/app/containers/PlaylistViewContainer/PlaylistViewContainer.test.tsx

Summary

Maintainability
F
3 days
Test Coverage
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { fireEvent, render, waitFor } from '@testing-library/react';
import React from 'react';
import { createMemoryHistory } from 'history';
import { store as electronStore } from '@nuclear/core';

import { buildElectronStoreState, buildStoreState } from '../../../test/storeBuilders';
import { AnyProps, configureMockStore, setupI18Next, TestRouterProvider, TestStoreProvider } from '../../../test/testUtils';
import MainContentContainer from '../MainContentContainer';
import { onReorder } from '.';

jest.mock('fs');
jest.mock('electron-store');

describe('Playlist view container', () => {
  beforeAll(() => {
    setupI18Next();
  });

  beforeEach(() => {
    electronStore.clear();
  });

  it('should display a playlist', () => {
    const { component } = mountComponent();
    expect(component.asFragment()).toMatchSnapshot();
  });

  it('should play all tracks in the playlist', async () => {
    const { component, store } = mountComponent();
    await waitFor(() => component.getByText('Play').click());

    const state = store.getState();
    expect(state.queue.queueItems).toEqual([
      expect.objectContaining({
        artist: 'test artist 1',
        name: 'test track'
      }),
      expect.objectContaining({
        artist: 'test artist 2',
        name: 'test track 22'
      })
    ]);
    expect(state.player.playbackStatus).toEqual('PLAYING');
  });

  it('should add all tracks in the playlist to the queue', async () => {
    const { component, store } = mountComponent();
    await waitFor(() => component.getByTestId('more-button').click());
    await waitFor(() => component.getByText(/Add playlist to queue/i).click());

    const state = store.getState();
    expect(state.queue.queueItems).toEqual([
      expect.objectContaining({
        artist: 'test artist 1',
        name: 'test track'
      }),
      expect.objectContaining({
        artist: 'test artist 2',
        name: 'test track 22'
      })
    ]);
    expect(state.player.playbackStatus).toEqual('PAUSED');
  });

  it('should delete the playlist', async () => {
    const { component, store } = mountComponent();

    let state = store.getState();
    expect(state.playlists.localPlaylists.data).toHaveLength(2);

    await waitFor(() => component.getByTestId('more-button').click());
    await waitFor(() => component.getByText(/delete/i).click());

    state = store.getState();
    expect(state.playlists.localPlaylists.data).toHaveLength(1);
  });

  it('should export the playlist', async () => {
    const { component, store } = mountComponent();
    await waitFor(() => component.getByTestId('more-button').click());
    await waitFor(() => component.getByText(/export/i).click());
    const state = store.getState();
    const remote = require('electron').remote;
    const fs = require('fs');
    const [playlist] = state.playlists.localPlaylists.data;
    // check if the dialog was open
    expect(remote.dialog.showSaveDialog).toHaveBeenCalledWith({
      defaultPath: playlist.name,
      filters: [
        { name: 'file', extensions: ['json'] }
      ],
      properties: ['createDirectory', 'showOverwriteConfirmation']
    });
    expect(remote.dialog.showSaveDialog).toHaveBeenCalledTimes(1);
    // check if the playlist was properly exported 
    expect(fs.writeFile).toHaveBeenCalledWith(
      'downloaded_playlist',
      JSON.stringify(playlist, null, 2),
      expect.any(Function)
    );
  });

  it('should rename the playlist', async () => {
    const { component, store } = mountComponent();
    await waitFor(() => component.getByTestId('rename-button').click());
    const input = component.getByPlaceholderText('Playlist name...');
    fireEvent.change(input, { target: { value: 'new name' } });
    await waitFor(() => component.getByText(/rename/i).click());

    const state = store.getState();
    expect(state.playlists.localPlaylists.data[0].name).toEqual('new name');
  });

  it('should update the last modified date when the playlist is modified', async () => {
    Date.now = jest.fn(() => (new Date('2021-01-01').valueOf()));
    const { component, store } = mountComponent();
    await waitFor(() => component.getByTestId('rename-button').click());
    const input = component.getByPlaceholderText('Playlist name...');
    fireEvent.change(input, { target: { value: 'new name' } });
    await waitFor(() => component.getByText(/rename/i).click());

    const state = store.getState();
    expect(state.playlists.localPlaylists.data[0].lastModified).toEqual(new Date('2021-01-01').valueOf());
  });

  it('should add a single track to the queue', async () => {
    const { component, store } = mountComponent();
    await waitFor(() => component.getAllByTestId('add-to-queue')[1].click());

    const state = store.getState();
    expect(state.queue.queueItems).toEqual([
      expect.objectContaining({
        artist: 'test artist 2',
        name: 'test track 22'
      })
    ]);
  });

  it('should play a single track', async () => {
    const { component, store } = mountComponent();
    await waitFor(() => component.getAllByTestId('play-now')[1].click());

    const state = store.getState();
    expect(state.queue.queueItems).toEqual([
      expect.objectContaining({
        artist: 'test artist 2',
        name: 'test track 22'
      })
    ]);
    expect(state.player.playbackStatus).toEqual('PLAYING');
  });

  it('should add a single track to favorites', async () => {
    const { component, store } = mountComponent();
    await waitFor(() => component.getAllByTestId('track-popup-trigger')[1].click());
    await waitFor(() => component.getByText(/add to favorites/i).click());

    const state = store.getState();
    expect(state.favorites.tracks).toEqual([
      expect.objectContaining({
        artist: 'test artist 2',
        name: 'test track 22'
      })
    ]);
  });

  it('should add a single track to downloads', async () => {
    const { component, store } = mountComponent();
    await waitFor(() => component.getAllByTestId('track-popup-trigger')[1].click());
    await waitFor(() => component.getByText(/download/i).click());

    const state = store.getState();
    expect(state.downloads).toEqual([
      expect.objectContaining({
        completion: 0,
        status: 'Waiting',
        track: expect.objectContaining({
          artist: 'test artist 2',
          name: 'test track 22'
        })
      })
    ]);
  });

  it('should delete a single track from the playlist', async () => {
    const { component, store } = mountComponent();
    await waitFor(() => component.getAllByTestId('delete-button')[0].click());

    const state = store.getState();
    expect(state.playlists.localPlaylists.data[0].tracks).toHaveLength(1);
    expect(state.playlists.localPlaylists.data[0].tracks).toEqual([
      expect.objectContaining({
        artist: 'test artist 2',
        name: 'test track 22'
      })
    ]);
  });

  it('should delete a single track from the playlist when there are its duplicates', async () => {
    const initialStore = buildStoreState()
      .withPlugins()
      .withPlaylists([{
        id: 'test-playlist-id',
        name: 'test playlist',
        lastModified: 1000198000000,
        tracks: [{
          uuid: 'test-track-uuid-1',
          artist: 'test artist 1',
          name: 'test track 1',
          stream: undefined
        }, {
          uuid: 'test-track-uuid-1',
          artist: 'test artist 1',
          name: 'test track 1',
          stream: undefined
        }, {
          uuid: 'test-track-uuid-3',
          artist: 'test artist 2',
          name: 'test track 2',
          stream: undefined
        }]}])
      .withConnectivity()
      .build();
    const { component, store } = mountComponent(initialStore);
    await waitFor(() => component.getAllByTestId('delete-button')[1].click());

    const state = store.getState();
    expect(state.playlists.localPlaylists.data[0].tracks).toHaveLength(2);
    expect(state.playlists.localPlaylists.data[0].tracks).toEqual([
      expect.objectContaining({
        uuid: 'test-track-uuid-1',
        artist: 'test artist 1',
        name: 'test track 1'
      }),
      expect.objectContaining({
        uuid: 'test-track-uuid-3',
        artist: 'test artist 2',
        name: 'test track 2'
      })
    ]);
  });

  const mountComponent = (initialStore?: AnyProps, initStore = true) => {
    const initialState = initialStore ||
      buildStoreState()
        .withPlugins()
        .withPlaylists()
        .withConnectivity()
        .build();

    // @ts-ignore
    initStore && electronStore.init({
      ...buildElectronStoreState(),
      playlists: initialState.playlists.localPlaylists.data
    });

    const history = createMemoryHistory({
      initialEntries: ['/playlist/test-playlist-id']
    });

    const store = configureMockStore(initialState);

    const component = render(
      <TestRouterProvider
        history={history}
      >
        <TestStoreProvider
          store={store}
        >
          <MainContentContainer />
        </TestStoreProvider>
      </TestRouterProvider >
    );

    return { component, history, store };
  };
});

describe('Playlist view container - utils', () => {
  let playlist = {
    tracks: [1, 2, 3, 4]
  };

  let newPlaylist = {};
  const updatePlaylist = playlistState => {
    newPlaylist = playlistState;
  };

  let reorder: ReturnType<typeof onReorder>;

  beforeEach(() => {
    newPlaylist = {};
    playlist = {
      tracks: [1, 2, 3, 4]
    };
    reorder = onReorder(playlist, updatePlaylist);
  });
  it('should reorder tracks correctly (1)', () => {
    reorder(0, 2);
    expect(newPlaylist).toEqual({
      tracks: [2, 3, 1, 4]
    });
  });

  it('should reorder tracks correctly (2)', () => {
    reorder(1, 3);
    expect(newPlaylist).toEqual({
      tracks: [1, 3, 4, 2]
    });
  });

  it('should reorder tracks correctly (3)', () => {
    reorder(0, 1);
    expect(newPlaylist).toEqual({
      tracks: [2, 1, 3, 4]
    });
  });
});