martijnversluis/ChordSheetJS

View on GitHub
test/chord_sheet/song.test.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { ChordLyricsPair, ChordSheetSerializer, Tag } from '../../src';

import {
  createChordDefinition,
  createChordLyricsPair,
  createLine,
  createSong, createTag,
} from '../utilities';

import { exampleSongSolfege, exampleSongSymbol } from '../fixtures/song';
import { serializedSongSolfege, serializedSongSymbol } from '../fixtures/serialized_song';
import { changedSongSolfege, changedSongSymbol } from '../fixtures/changed_song';
import Song from '../../src/chord_sheet/song';

const createLineStub = ({ renderable }) => (
  {
    hasRenderableItems() {
      return renderable;
    },

    hasContent() {
      return renderable;
    },

    isEmpty() {
      return false;
    },
  }
);

describe('Song', () => {
  it('can have a title', () => {
    const song = new Song({ title: 'Song title' });

    expect(song.title).toEqual('Song title');
  });

  it('can have a subtitle', () => {
    const song = new Song({ subtitle: 'Song subtitle' });

    expect(song.subtitle).toEqual('Song subtitle');
  });

  it('can have an artist', () => {
    const song = new Song({ artist: 'Song artist' });

    expect(song.artist).toEqual('Song artist');
  });

  it('can have a composer', () => {
    const song = new Song({ composer: 'Song composer' });

    expect(song.composer).toEqual('Song composer');
  });

  it('can have a lyricist', () => {
    const song = new Song({ lyricist: 'Song lyricist' });

    expect(song.lyricist).toEqual('Song lyricist');
  });

  it('can have a copyright', () => {
    const song = new Song({ copyright: 'Song copyright' });

    expect(song.copyright).toEqual('Song copyright');
  });

  it('can have an album', () => {
    const song = new Song({ album: 'Song album' });

    expect(song.album).toEqual('Song album');
  });

  it('can have a year', () => {
    const song = new Song({ year: 'Song year' });

    expect(song.year).toEqual('Song year');
  });

  it('can have a key', () => {
    const song = new Song({ key: 'Song key' });

    expect(song.key).toEqual('Song key');
  });

  it('can have a time', () => {
    const song = new Song({ time: 'Song time' });

    expect(song.time).toEqual('Song time');
  });

  it('can have a tempo', () => {
    const song = new Song({ tempo: 'Song tempo' });

    expect(song.tempo).toEqual('Song tempo');
  });

  it('can have a duration', () => {
    const song = new Song({ duration: 'Song duration' });

    expect(song.duration).toEqual('Song duration');
  });

  it('can have a capo', () => {
    const song = new Song({ capo: 'Song capo' });

    expect(song.capo).toEqual('Song capo');
  });

  describe('#clone', () => {
    it('returns a clone of the symbol song', () => {
      const serializedExampleSong = new ChordSheetSerializer().serialize(exampleSongSymbol);
      const clone = exampleSongSymbol.clone();
      const serializedClone = new ChordSheetSerializer().serialize(clone);
      expect(serializedClone).toEqual(serializedExampleSong);
    });

    it('returns a clone of the solfege song', () => {
      const serializedExampleSong = new ChordSheetSerializer().serialize(exampleSongSolfege);
      const clone = exampleSongSolfege.clone();
      const serializedClone = new ChordSheetSerializer().serialize(clone);
      expect(serializedClone).toEqual(serializedExampleSong);
    });
  });

  describe('#bodyLines', () => {
    it('returns the lines excluding leading non-renderable lines', () => {
      const nonRenderableLine1 = createLineStub({ renderable: false });
      const nonRenderableLine2 = createLineStub({ renderable: false });
      const renderableLine1 = createLineStub({ renderable: true });
      const nonRenderableLine3 = createLineStub({ renderable: false });
      const song = createSong([nonRenderableLine1, nonRenderableLine2, renderableLine1, nonRenderableLine3]);

      expect(song.bodyLines).toEqual([renderableLine1, nonRenderableLine3]);
    });
  });

  describe('#mapLines', () => {
    it('changes the symbol song', () => {
      const song = exampleSongSymbol.clone();

      [0, 1, 3, 2].forEach((expectedLineCount, index) => {
        expect(song.paragraphs[index].lines).toHaveLength(expectedLineCount);
      });

      const changedSong = song.mapLines((line) => (
        line.mapItems((item) => {
          if (item instanceof ChordLyricsPair) {
            return item
              .transpose(2, 'D')
              .setLyrics((item.lyrics || '').toUpperCase())
              .setAnnotation((item.annotation || '').toUpperCase());
          }

          if (item instanceof Tag) {
            let changedTag = item.set({ value: `${item.value} changed` });

            if (item.attributes.label) {
              changedTag = changedTag.setAttribute('label', `${item.attributes.label} changed`);
            }

            return changedTag;
          }

          return item;
        })
      ));

      expect(new ChordSheetSerializer().serialize(changedSong)).toEqual(changedSongSymbol);
      expect(changedSong.title).toEqual('Let it be changed');
      expect(changedSong.subtitle).toEqual('ChordSheetJS example version changed');
      expect(changedSong.key).toEqual('C changed');
      expect(changedSong.composer).toEqual(['John Lennon changed', 'Paul McCartney changed']);
      expect(changedSong.paragraphs.length).toEqual(song.paragraphs.length);

      [0, 1, 3, 3].forEach((expectedLineCount, index) => {
        expect(changedSong.paragraphs[index].lines).toHaveLength(expectedLineCount);
      });
    });

    it('changes the solfege song', () => {
      const song = exampleSongSolfege.clone();

      [0, 1, 3, 2].forEach((expectedLineCount, index) => {
        expect(song.paragraphs[index].lines).toHaveLength(expectedLineCount);
      });

      const changedSong = song.mapLines((line) => (
        line.mapItems((item) => {
          if (item instanceof ChordLyricsPair) {
            return item
              .transpose(2, 'Re')
              .setLyrics((item.lyrics || '').toUpperCase())
              .setAnnotation((item.annotation || '').toUpperCase());
          }

          if (item instanceof Tag) {
            return item.set({ value: `${item.value} changed` });
          }

          return item;
        })
      ));

      expect(new ChordSheetSerializer().serialize(changedSong)).toEqual(changedSongSolfege);
      expect(changedSong.title).toEqual('Let it be changed');
      expect(changedSong.subtitle).toEqual('ChordSheetJS example version changed');
      expect(changedSong.key).toEqual('Do changed');
      expect(changedSong.composer).toEqual(['John Lennon changed', 'Paul McCartney changed']);
      expect(changedSong.paragraphs.length).toEqual(song.paragraphs.length);

      [0, 1, 3, 3].forEach((expectedLineCount, index) => {
        expect(changedSong.paragraphs[index].lines).toHaveLength(expectedLineCount);
      });
    });
  });

  describe('#mapItems', () => {
    it('changes the symbol song', () => {
      const song = exampleSongSymbol.clone();
      expect(song.paragraphs.map((p) => p.lines.length)).toEqual([0, 1, 3, 2, 2, 3, 3, 3, 2, 3]);

      const changedSong = song.mapItems((item) => {
        if (item instanceof ChordLyricsPair) {
          return item
            .transpose(2, 'D')
            .setLyrics((item.lyrics || '').toUpperCase())
            .setAnnotation((item.annotation || '').toUpperCase());
        }

        if (item instanceof Tag) {
          let changedTag = item.set({ value: `${item.value} changed` });

          if (item.attributes.label) {
            changedTag = changedTag.setAttribute('label', `${item.attributes.label} changed`);
          }

          return changedTag;
        }

        return item;
      });

      expect(new ChordSheetSerializer().serialize(changedSong)).toEqual(changedSongSymbol);
      expect(changedSong.title).toEqual('Let it be changed');
      expect(changedSong.subtitle).toEqual('ChordSheetJS example version changed');
      expect(changedSong.key).toEqual('C changed');
      expect(changedSong.composer).toEqual(['John Lennon changed', 'Paul McCartney changed']);
      expect(changedSong.paragraphs.length).toEqual(song.paragraphs.length);

      [0, 1, 3, 3].forEach((expectedLineCount, index) => {
        expect(changedSong.paragraphs[index].lines).toHaveLength(expectedLineCount);
      });
    });

    it('changes the solfege song', () => {
      const song = exampleSongSolfege.clone();
      expect(song.paragraphs.map((p) => p.lines.length)).toEqual([0, 1, 3, 2, 2, 3, 3, 3, 2, 3]);

      const changedSong = song.mapItems((item) => {
        if (item instanceof ChordLyricsPair) {
          return item
            .transpose(2, 'Re')
            .setLyrics((item.lyrics || '').toUpperCase())
            .setAnnotation((item.annotation || '').toUpperCase());
        }

        if (item instanceof Tag) {
          return item.set({ value: `${item.value} changed` });
        }

        return item;
      });

      expect(new ChordSheetSerializer().serialize(changedSong)).toEqual(changedSongSolfege);
      expect(changedSong.title).toEqual('Let it be changed');
      expect(changedSong.subtitle).toEqual('ChordSheetJS example version changed');
      expect(changedSong.key).toEqual('Do changed');
      expect(changedSong.composer).toEqual(['John Lennon changed', 'Paul McCartney changed']);
      expect(changedSong.paragraphs.length).toEqual(song.paragraphs.length);

      [0, 1, 3, 3].forEach((expectedLineCount, index) => {
        expect(changedSong.paragraphs[index].lines).toHaveLength(expectedLineCount);
      });
    });
  });

  it('symbol can be serialized', () => {
    expect(new ChordSheetSerializer().serialize(exampleSongSymbol)).toEqual(serializedSongSymbol);
  });

  it('symbol can be deserialized', () => {
    expect(new ChordSheetSerializer().deserialize(serializedSongSymbol)).toEqual(exampleSongSymbol);
  });

  it('solfege can be serialized', () => {
    expect(new ChordSheetSerializer().serialize(exampleSongSolfege)).toEqual(serializedSongSolfege);
  });

  it('solfege can be deserialized', () => {
    expect(new ChordSheetSerializer().deserialize(serializedSongSolfege)).toEqual(exampleSongSolfege);
  });

  describe('#getChords', () => {
    it('returns the unique chords in a song', () => {
      const song = createSong([
        createLine([
          createChordLyricsPair('CM7', 'let'),
          createChordLyricsPair('', 'it'),
          createChordLyricsPair('Dm7', ''),
          createChordLyricsPair('Dm7   ', ''),
        ]),
        createLine([]),
        createLine([
          createChordLyricsPair('F#', 'be'),
          createChordLyricsPair('d#', 'be'),
          createChordLyricsPair('     F#', 'be'),
          createChordLyricsPair('', 'changed'),
        ]),
      ]);

      expect(song.getChords()).toEqual(['CM7', 'Dm7', 'F#', 'D#']);
    });

    it('returns an empty array if there are no chords in the song', () => {
      const song = createSong([
        createLine([
          createChordLyricsPair('', 'let'),
          createChordLyricsPair('', 'it'),
          createChordLyricsPair('', ''),
        ]),
        createLine([]),
        createLine([
          createChordLyricsPair('', 'be'),
          createChordLyricsPair('', 'changed'),
        ]),
      ]);

      expect(song.getChords()).toEqual([]);
    });
  });

  describe('#getChordDefinitions', () => {
    it('returns the unique chord definitions in a song', () => {
      const cm7 = createChordDefinition('CM7', 3, ['x', '0', 1]);
      const dm = createChordDefinition('Dm', 3, ['x', 3, 5]);

      const song = createSong([
        createLine([
          createTag('chord', 'CM7', cm7),
        ]),
        createLine([]),
        createLine([
          createTag('define', 'Dm', dm),
        ]),
      ]);

      expect(song.getChordDefinitions()).toEqual({
        CM7: cm7,
        Dm: dm,
      });
    });

    it('returns an empty array if there are no chords in the song', () => {
      const song = createSong([
        createLine([
          createTag('chord', 'CM7'),
        ]),
        createLine([]),
        createLine([
          createChordLyricsPair('Am', 'be'),
        ]),
      ]);

      expect(song.getChordDefinitions()).toEqual({});
    });
  });
});