src/__test__/Domain/Chord.test.ts
import * as fc from 'fast-check';
import { ChordFunction, ChordPattern, ClosedChord } from '../../Domain/Chord';
import { Duration } from '../../Domain/Duration';
import { Octave } from '../../Domain/Note';
import { Accidental, Pitch } from '../../Domain/Pitch';
describe('Major Chords should', () => {
test('have a name', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major, Duration.Quarter, Octave.C4);
expect(chord.Name).toBe('CMajor');
});
test('have a root', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major, Duration.Quarter, Octave.C4);
expect(chord.pitchForFunction(ChordFunction.Root)).toBe(Pitch.C);
});
test('have a third', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major, Duration.Quarter, Octave.C4);
expect(chord.pitchForFunction(ChordFunction.Third)).toBe(Pitch.E);
});
test('have a fifth', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major, Duration.Quarter, Octave.C4);
expect(chord.pitchForFunction(ChordFunction.Fifth)).toBe(Pitch.G);
});
test('have a seventh', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
expect(chord.pitchForFunction(ChordFunction.Seventh)).toBe(Pitch.B);
});
test('have a ninth', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major9, Duration.Quarter, Octave.C4);
expect(chord.pitchForFunction(ChordFunction.Ninth)).toBe(Pitch.D);
});
test('have an eleventh', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major11, Duration.Quarter, Octave.C4);
expect(chord.pitchForFunction(ChordFunction.Eleventh)).toBe(Pitch.F);
});
test('have a thirteenth', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major13, Duration.Quarter, Octave.C4);
expect(chord.pitchForFunction(ChordFunction.Thirteenth)).toBe(Pitch.A);
});
test('Have expected pitches for C Major chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Major, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C Major 6 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.A];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Major6, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C Major 6 add 9 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.A, Pitch.D];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Major6Add9, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C Major 7 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.B];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C Major 9 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.B, Pitch.D];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Major9, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C Major 11 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.B, Pitch.F];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Major11, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C Major 13#11 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.B, Pitch.FSharp, Pitch.A];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Major13Sharp11, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C Major 9#11 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.B, Pitch.D, Pitch.FSharp];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Major9Sharp11, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for CMaj7#5 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.GSharp, Pitch.B];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Augmented7, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for CAug chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.GSharp];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Augmented, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
});
describe('Dominant Chords should', () => {
test('Have expected pitches for C7 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.BFlat];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Dominant7, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C9 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.BFlat, Pitch.D];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Dominant9, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C7b5b9 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.GFlat, Pitch.BFlat, Pitch.DFlat];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Dominant7Flat5Flat9, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C7b5#9 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.GFlat, Pitch.BFlat, Pitch.DSharp];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Dominant7Flat5Sharp9, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C11 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.BFlat, Pitch.D, Pitch.F];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Dominant11, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C13 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.BFlat, Pitch.D, Pitch.F, Pitch.A];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Dominant13, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C7#9 chord', () => {
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.BFlat, Pitch.DSharp];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Dominant7Sharp9, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
describe('Minor Chords should', () => {
test('Have expected pitches for C- chord', () => {
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.G];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Minor, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C-6 chord', () => {
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.G, Pitch.A];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Minor6, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C-6add9 chord', () => {
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.G, Pitch.A, Pitch.D];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Minor6Add9, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C-7 chord', () => {
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.G, Pitch.BFlat];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Minor7, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C-Maj7 chord', () => {
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.G, Pitch.B];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.MinorMaj7, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C-9 chord', () => {
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.G, Pitch.BFlat, Pitch.D];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Minor9, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C-Maj9 chord', () => {
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.G, Pitch.B, Pitch.D];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.MinorMaj9, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for C-7b5 chord', () => {
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.GFlat, Pitch.BFlat];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Minor7b5, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
});
describe('Diminished Chords should', () => {
test('Have expected pitches for Cdim chord', () => {
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.GFlat];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Diminished, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for Cdim7 chord', () => {
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.GFlat, Pitch.A];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Diminished7, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
});
describe('Sus Chords should', () => {
test('Have expected pitches for CSus2 chord', () => {
const expectedPitches = [Pitch.C, Pitch.D, Pitch.G];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Sus2, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for CSus2dim chord', () => {
const expectedPitches = [Pitch.C, Pitch.D, Pitch.GFlat];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Sus2Diminished, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for CSus2aug chord', () => {
const expectedPitches = [Pitch.C, Pitch.D, Pitch.GSharp];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Sus2Augmented, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for CSus4 chord', () => {
const expectedPitches = [Pitch.C, Pitch.F, Pitch.G];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Sus4, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for CSus4dim chord', () => {
const expectedPitches = [Pitch.C, Pitch.F, Pitch.GFlat];
const chordPitches = Array.from(
new ClosedChord(Pitch.C, ChordPattern.Sus4Diminished, Duration.Quarter, Octave.C4)
);
expect(chordPitches).toStrictEqual(expectedPitches);
});
test('Have expected pitches for CSus4aug chord', () => {
const expectedPitches = [Pitch.C, Pitch.F, Pitch.GSharp];
const chord = new ClosedChord(
Pitch.C,
ChordPattern.Sus4Augmented,
Duration.Quarter,
Octave.C4
);
expect(Array.from(chord)).toStrictEqual(expectedPitches);
});
});
});
describe('Chords should', () => {
test('be named after the root note', () => {
const name = new ClosedChord(Pitch.C, ChordPattern.Diminished, Duration.Quarter, Octave.C4)
.Name;
expect(name[0]).toBe('C');
});
test('be named after the root note and the quality', () => {
const name = new ClosedChord(Pitch.C, ChordPattern.Minor7b5, Duration.Quarter, Octave.C4).Name;
expect(name).toBe('CMinor 7 b5');
});
test('be named after the root note and the abbreviated quality', () => {
const name = new ClosedChord(Pitch.C, ChordPattern.Diminished7, Duration.Quarter, Octave.C4)
.Abbreviation;
expect(name).toBe('Cdim7');
});
test('have bass note equal to root when not inverted', () => {
const expectedPitch = Pitch.C;
const pitch = new ClosedChord(Pitch.C, ChordPattern.Diminished, Duration.Quarter, Octave.C4)
.Bass;
expect(pitch).toStrictEqual(expectedPitch);
});
test('Have lead note equal to fifth for triads when not inverted', () => {
const expectedPitch = Pitch.GFlat;
const pitch = new ClosedChord(Pitch.C, ChordPattern.Diminished, Duration.Quarter, Octave.C4)
.Lead;
expect(pitch).toStrictEqual(expectedPitch);
});
test('be able to identify Pitch for root', () => {
const expectedPitch = Pitch.C;
const pitch = new ClosedChord(
Pitch.C,
ChordPattern.Diminished,
Duration.Quarter,
Octave.C4
).pitchForFunction(ChordFunction.Root);
expect(pitch).toStrictEqual(expectedPitch);
});
test('be able to identify Pitch for third', () => {
const expectedPitch = Pitch.EFlat;
const pitch = new ClosedChord(
Pitch.C,
ChordPattern.Diminished,
Duration.Quarter,
Octave.C4
).pitchForFunction(ChordFunction.Third);
expect(pitch).toStrictEqual(expectedPitch);
});
test('be able to identify Pitch for third on sus2 chords', () => {
const expectedPitch = Pitch.D;
const pitch = new ClosedChord(
Pitch.C,
ChordPattern.Sus2,
Duration.Quarter,
Octave.C4
).pitchForFunction(ChordFunction.Third);
expect(pitch).toStrictEqual(expectedPitch);
});
test('be able to identify Pitch for third on sus4 chords', () => {
const expectedPitch = Pitch.F;
const pitch = new ClosedChord(
Pitch.C,
ChordPattern.Sus4,
Duration.Quarter,
Octave.C4
).pitchForFunction(ChordFunction.Third);
expect(pitch).toStrictEqual(expectedPitch);
});
test('be able to identify Pitch for fifth', () => {
const expectedPitch = Pitch.GFlat;
const pitch = new ClosedChord(
Pitch.C,
ChordPattern.Diminished,
Duration.Quarter,
Octave.C4
).pitchForFunction(ChordFunction.Fifth);
expect(pitch).toStrictEqual(expectedPitch);
});
test('be able to remove Pitch for fifth', () => {
const baseChord = new ClosedChord(
Pitch.C,
ChordPattern.Diminished7,
Duration.Quarter,
Octave.C4
);
const chord = baseChord.remove(ChordFunction.Fifth);
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.A];
expect(Array.from(chord)).toStrictEqual(expectedPitches);
});
test('be able to remove Pitch for fifth on drop 2 chord', () => {
const baseChord = new ClosedChord(
Pitch.C,
ChordPattern.Major7,
Duration.Quarter,
Octave.C4
).drop2();
const chord = baseChord.remove(ChordFunction.Fifth);
const expectedPitches = [Pitch.C, Pitch.B, Pitch.E];
expect(Array.from(chord).map((p) => p.Name)).toStrictEqual(expectedPitches.map((p) => p.Name));
});
test('be able to remove Pitch for fifth on drop 3 chord', () => {
const baseChord = new ClosedChord(
Pitch.C,
ChordPattern.Major7,
Duration.Quarter,
Octave.C4
).drop3();
const chord = baseChord.remove(ChordFunction.Fifth);
const expectedPitches = [Pitch.C, Pitch.B, Pitch.E];
expect(Array.from(chord)).toStrictEqual(expectedPitches);
});
test('be able to remove Pitch for seventh', () => {
const baseChord = new ClosedChord(
Pitch.C,
ChordPattern.Diminished7,
Duration.Quarter,
Octave.C4
);
const chord = baseChord.remove(ChordFunction.Seventh);
const expectedPitches = [Pitch.C, Pitch.EFlat, Pitch.GFlat];
expect(Array.from(chord)).toStrictEqual(expectedPitches);
});
test('be able to be inverted to first inversion', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.E, Pitch.G, Pitch.C];
expect(Array.from(chord.invert())).toStrictEqual(expectedPitches);
});
test('be able to be inverted to second inversion', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.G, Pitch.C, Pitch.E];
expect(Array.from(chord.invert().invert())).toStrictEqual(expectedPitches);
});
test('be able to be inverted to third inversion', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.B, Pitch.C, Pitch.E, Pitch.G];
expect(Array.from(chord.invert().invert().invert())).toStrictEqual(expectedPitches);
});
test('be able to be transformed into drop 2 chords', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.C, Pitch.G, Pitch.B, Pitch.E];
expect(Array.from(chord.drop2())).toStrictEqual(expectedPitches);
});
test('not be able to be transformed into drop 2 chords when its a 3 note chord', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G];
expect(Array.from(chord.drop2())).toStrictEqual(expectedPitches);
});
test('be able to be transformed drop 2 chords to closed chords', () => {
const chord = new ClosedChord(
Pitch.C,
ChordPattern.Major7,
Duration.Quarter,
Octave.C4
).drop2();
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.B];
expect(Array.from(chord.closed())).toStrictEqual(expectedPitches);
});
test('be able to be transformed into drop 3 chords', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.C, Pitch.B, Pitch.E, Pitch.G];
expect(Array.from(chord.drop3())).toStrictEqual(expectedPitches);
});
test('not be able to be transformed into drop 3 chords when its a 3 note chord', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G];
expect(Array.from(chord.drop3())).toStrictEqual(expectedPitches);
});
test('be able to be transformed drop 3 chords to closed chords', () => {
const chord = new ClosedChord(
Pitch.C,
ChordPattern.Major7,
Duration.Quarter,
Octave.C4
).drop3();
const expectedPitches = [Pitch.C, Pitch.E, Pitch.G, Pitch.B];
expect(Array.from(chord.closed())).toStrictEqual(expectedPitches);
});
test('be able to be inverted to first inversion when in drop2 format', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.E, Pitch.B, Pitch.C, Pitch.G];
expect(Array.from(chord.drop2().invert()).map((p) => p.Name)).toStrictEqual(
expectedPitches.map((p) => p.Name)
);
});
test('be able to be inverted to second inversion when in drop2 format', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.G, Pitch.C, Pitch.E, Pitch.B];
expect(Array.from(chord.drop2().invert().invert()).map((p) => p.Name)).toStrictEqual(
expectedPitches.map((p) => p.Name)
);
});
test('be able to be inverted to third inversion when in drop2 format', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.B, Pitch.E, Pitch.G, Pitch.C];
expect(Array.from(chord.drop2().invert().invert().invert()).map((p) => p.Name)).toStrictEqual(
expectedPitches.map((p) => p.Name)
);
});
test('be able to be inverted to first inversion when in drop3 format', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.E, Pitch.C, Pitch.G, Pitch.B];
expect(Array.from(chord.drop3().invert()).map((p) => p.Name)).toStrictEqual(
expectedPitches.map((p) => p.Name)
);
});
test('be able to be inverted to second inversion when in drop3 format', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.G, Pitch.E, Pitch.B, Pitch.C];
expect(Array.from(chord.drop3().invert().invert()).map((p) => p.Name)).toStrictEqual(
expectedPitches.map((p) => p.Name)
);
});
test('be able to be inverted to third inversion when in drop3 format', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const expectedPitches = [Pitch.B, Pitch.G, Pitch.C, Pitch.E];
expect(Array.from(chord.drop3().invert().invert().invert()).map((p) => p.Name)).toStrictEqual(
expectedPitches.map((p) => p.Name)
);
});
test('be able to create chord from primitive types', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Augmented, Duration.Quarter, Octave.C4);
const from = ClosedChord.From(chord.To);
expect(chord.Name).toBe(from?.Name);
});
test('not be able to create chord from invalid primitive types', () => {
const chordPrimitives = {
name: 'invalid',
abbreviation: 'invalid',
root: Pitch.C.To,
pitches: [{ pitch: Pitch.C.To, function: ChordFunction.Root.To }],
pattern: 'invalid',
bass: Pitch.C.To,
lead: Pitch.C.To,
duration: Duration.Whole.To,
octave: Octave.C0.To,
};
expect(() => ClosedChord.From(chordPrimitives)).toThrow();
});
test('Converting a drop 2 chord to drop 2 returns same chord', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const drop = chord.drop2();
const other = drop.drop2();
expect(drop).toBe(other);
});
test('Converting a drop 3 chord to drop 3 returns same chord', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const drop = chord.drop3();
const other = drop.drop3();
expect(drop).toBe(other);
});
test('Converting a closed chord to closed returns same chord', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
const other = chord.closed();
expect(chord).toBe(other);
});
test('not be able to create chord from invalid primitive', () => {
const chordPrimitives = {
name: 'C Major',
abbreviation: 'CM',
root: { name: 'V', naturalName: 'V', value: 0, accidental: Accidental.Natural },
pitches: [],
pattern: 'Major',
bass: { name: 'H', naturalName: 'H', value: 0, accidental: Accidental.Natural },
lead: { name: 'Z', naturalName: 'Z', value: 0, accidental: Accidental.Natural },
duration: Duration.Quarter.To,
octave: Octave.C4.To,
};
expect(() => ClosedChord.From(chordPrimitives)).toThrow();
});
test('has a duration', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
expect(chord.Duration).toBe(Duration.Quarter);
expect(chord.DurationName).toBe(Duration.Quarter.Name);
expect(chord.DurationValue).toBe(Duration.Quarter.value);
expect(chord.tick).toBe(Duration.Quarter.tick);
});
test('to have pitches', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
expect([...chord.Pitches]).toHaveLength(4);
});
test('does have an octave', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C3);
expect([...chord.Octaves].pop()).toBe(Octave.C3);
expect([...chord.OctaveNames].pop()).toBe(Octave.C3.Name);
});
test('does have a note', () => {
const chord = new ClosedChord(Pitch.C, ChordPattern.Major7, Duration.Quarter, Octave.C4);
expect([...chord.Notes]).toHaveLength(4);
expect([...chord.MidiNumbers]).toHaveLength(4);
});
});
describe('Chord properties', () => {
test('chords inverted by the number of pitches become base root chords', () => {
fc.assert(
fc.property(
fc.constantFrom(...Pitch.pitches),
fc.constantFrom(...ChordPattern.patterns),
(root: Pitch, pattern: ChordPattern) => {
const chord = new ClosedChord(root, pattern, Duration.Quarter, Octave.C4);
const expectedChordPitches = Array.from(chord);
const inversions = expectedChordPitches.length;
let invertedChord = chord.invert();
[...Array<ClosedChord>(inversions - 1)].forEach(() => {
invertedChord = invertedChord.invert();
});
expect(Array.from(invertedChord)).toStrictEqual(expectedChordPitches);
}
),
{ verbose: true }
);
});
test('drop 2 chords inverted by the number of pitches become base root chords', () => {
fc.assert(
fc.property(
fc.constantFrom(...Pitch.pitches),
fc.constantFrom(...ChordPattern.patterns),
(root: Pitch, pattern: ChordPattern) => {
const chord = new ClosedChord(root, pattern, Duration.Quarter, Octave.C4).drop2();
const expectedChordPitches = Array.from(chord);
const inversions = expectedChordPitches.length;
if (inversions > 3) return;
let invertedChord = chord.invert();
[...Array<ClosedChord>(inversions - 1)].forEach(() => {
invertedChord = invertedChord.invert();
});
expect(Array.from(invertedChord).map((p) => p.Name)).toStrictEqual(
expectedChordPitches.map((p) => p.Name)
);
}
),
{ verbose: true }
);
});
test('drop 3 chords inverted by the number of pitches become base root chords', () => {
fc.assert(
fc.property(
fc.constantFrom(...Pitch.pitches),
fc.constantFrom(...ChordPattern.patterns),
(root: Pitch, pattern: ChordPattern) => {
const chord = new ClosedChord(root, pattern, Duration.Quarter, Octave.C4).drop3();
const expectedChordPitches = Array.from(chord);
const inversions = expectedChordPitches.length;
if (inversions > 3) return;
let invertedChord = chord.invert();
[...Array<ClosedChord>(inversions - 1)].forEach(() => {
invertedChord = invertedChord.invert();
});
expect(Array.from(invertedChord).map((p) => p.Name)).toStrictEqual(
expectedChordPitches.map((p) => p.Name)
);
}
),
{ verbose: true }
);
});
test('chord can be converted to and from ChordPrimitives', () => {
fc.assert(
fc.property(
fc.constantFrom(...Pitch.pitches),
fc.constantFrom(...ChordPattern.patterns),
(root: Pitch, pattern: ChordPattern) => {
const chord = new ClosedChord(root, pattern, Duration.Quarter, Octave.C4);
const primitivesChord = chord.To;
const from = ClosedChord.From(primitivesChord);
expect(chord.Name).toBe(from?.Name);
expect(Array.from(chord).map((p) => p.To)).toStrictEqual(
primitivesChord.pitches.map((p) => p.pitch)
);
}
),
{ verbose: true }
);
});
});