vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature.
489 lines (488 loc) • 18.8 kB
JavaScript
import { VexFlowTests } from './vexflow_test_helpers.js';
import { Articulation } from '../src/articulation.js';
import { EasyScore } from '../src/easyscore.js';
import { FretHandFinger } from '../src/frethandfinger.js';
import { Modifier } from '../src/modifier.js';
import { Parenthesis } from '../src/parenthesis.js';
import { StaveConnector } from '../src/staveconnector.js';
import { Stem } from '../src/stem.js';
import { Tuplet } from '../src/tuplet.js';
const EasyScoreTests = {
Start() {
QUnit.module('EasyScore');
QUnit.test('Basic', basic);
QUnit.test('Accidentals', accidentals);
QUnit.test('Durations', durations);
QUnit.test('Chords', chords);
QUnit.test('Dots', dots);
QUnit.test('Types', types);
QUnit.test('Options', options);
const run = VexFlowTests.runTests;
run('Draw Basic', drawBasicTest);
run('Draw Different KeySignature', drawDiffKeysig);
run('Draw Basic Muted', drawBasicMutedTest);
run('Draw Basic Harmonic', drawBasicHarmonicTest);
run('Draw Basic Slash', drawBasicSlashTest);
run('Draw Ghostnote Basic', drawGhostBasicTest);
run('Draw Ghostnote Dotted', drawGhostDottedTest);
run('Draw Parenthesised', drawParenthesisedTest);
run('Draw Accidentals', drawAccidentalsTest);
run('Draw Beams', drawBeamsTest);
run('Draw Tuplets', drawTupletsTest);
run('Draw Dots', drawDotsTest);
run('Draw Options', drawOptionsTest);
run('Draw Fingerings', drawFingeringsTest);
run('Keys', keys);
},
};
function createShortcuts(score) {
return {
voice: score.voice.bind(score),
notes: score.notes.bind(score),
beam: score.beam.bind(score),
tuplet: score.tuplet.bind(score),
};
}
function basic(assert) {
const score = new EasyScore();
const mustPass = ['c4', 'c#4', 'c4/r', 'c#5', 'c3/m', 'c3//m', 'c3//h', 'c3/s', 'c3//s', 'c3/g', 'c3//g'];
const mustFail = ['', '()', '7', '(c#4 e5 g6'];
mustPass.forEach((line) => assert.equal(score.parse(line).success, true, line));
mustFail.forEach((line) => assert.equal(score.parse(line).success, false, line));
}
function accidentals(assert) {
const score = new EasyScore();
const mustPass = [
'c3',
'c##3, cb3',
'Cn3',
'f3//m',
'(c##3 cbb3 cn3), cb3',
'cbbs7',
'cbb7',
'cbss7',
'cbs7',
'cb7',
'cdb7',
'cd7',
'c##7',
'c#7',
'cn7',
'c++-7',
'c++7',
'c+-7',
'c+7',
'(cbs3 bbs3 dbs3), ebs3',
'(cd7 cbb3 cn3), cb3',
'co7',
'ck7',
];
const mustFail = [
'ct3',
'cdbb7',
'(cq cbb3 cn3), cb3',
'(cdd7 cbb3 cn3), cb3',
'cbbbs7',
'cbbss7',
'cbsss7',
'csbs7',
'cddb7',
'cddbb7',
'cdd7',
'c##b7',
'c#bs7',
'cnb#7',
'c+#+b-d7',
'c+--7',
'c++--7',
'c+++7',
'cbk7',
'cok7',
'cko7',
'c#s7',
];
mustPass.forEach((line) => assert.equal(score.parse(line).success, true, line));
mustFail.forEach((line) => assert.equal(score.parse(line).success, false, line));
}
function durations(assert) {
const score = new EasyScore();
const mustPass = ['c3/4', 'c##3/w, cb3', 'c##3/w, cb3/q', 'c##3/q, cb3/32', '(c##3 cbb3 cn3), cb3'];
const mustFail = ['Cn3/]', '/', '(cq cbb3 cn3), cb3', '(cdd7 cbb3 cn3), cb3'];
mustPass.forEach((line) => assert.equal(score.parse(line).success, true, line));
mustFail.forEach((line) => assert.equal(score.parse(line).success, false, line));
}
function chords(assert) {
const score = new EasyScore();
const mustPass = [
'(c5)',
'(c3 e0 g9)',
'(c##4 cbb4 cn4)/w, (c#5 cb2 a3)/32',
'(d##4 cbb4 cn4)/w/r, (c#5 cb2 a3)',
'(c##4 cbb4 cn4)/4, (c#5 cb2 a3)',
'(c##4 cbb4 cn4)/m, (c#5 cb2 a3)',
];
const mustFail = ['(c)'];
mustPass.forEach((line) => assert.equal(score.parse(line).success, true, line));
mustFail.forEach((line) => assert.equal(score.parse(line).success, false, line));
}
function dots(assert) {
const score = new EasyScore();
const mustPass = [
'c3/4.',
'c##3/w.., cb3',
'f##3/s, cb3/q...',
'c##3/q, cb3/32',
'(c##3 cbb3 cn3)., cb3',
'(c5).',
'(c##4 cbb4 cn4)/w.., (c#5 cb2 a3)/32',
];
const mustFail = ['.', 'c.#', 'c#4./4'];
mustPass.forEach((line) => assert.equal(score.parse(line).success, true, line));
mustFail.forEach((line) => assert.equal(score.parse(line).success, false, line));
}
function types(assert) {
const score = new EasyScore();
const mustPass = ['c3/4/m.', 'c##3//r.., cb3', 'c##3/m.., cb3', 'c##3/r.., cb3', 'd##3/w/s, cb3/q...', 'Fb4'];
const mustFail = ['c4/q/U', '(c##4, cbb4 cn4)/w.., (c#5 cb2 a3)/32', 'z#3'];
mustPass.forEach((line) => assert.equal(score.parse(line).success, true, line));
mustFail.forEach((line) => assert.equal(score.parse(line).success, false, line));
}
function options(assert) {
const score = new EasyScore();
const mustPass = [
'c3/4.[foo="bar"]',
'c##3/w.., cb3[id="blah"]',
'c##3/q, cb3/32',
'(c##3 cbb3 cn3).[blah="bod4o"], cb3',
'(c5)[fooooo="booo"]',
'c#5[id="foobar"]',
];
const mustFail = ['.[', 'f##3/w[], cb3/q...'];
mustPass.forEach((line) => assert.equal(score.parse(line).success, true, line));
mustFail.forEach((line) => assert.equal(score.parse(line).success, false, line));
}
function drawBasicTest(options) {
const f = VexFlowTests.makeFactory(options, 600, 350);
const score = f.EasyScore();
const system = f.System();
const { voice, notes } = createShortcuts(score);
system
.addStave({
voices: [
voice(notes('(d4 e4 g4)/q, c4/q, c4/q/r, c4/q', { stem: 'down' })),
voice(notes('c#5/h., c5/q', { stem: 'up' })),
],
})
.addClef('treble');
system
.addStave({
voices: [voice(notes('c#3/q, cn3/q, bb3/q, d##3/q', { clef: 'bass' }))],
})
.addClef('bass');
system.addConnector().setType(StaveConnector.type.BRACKET);
f.draw();
options.assert.expect(0);
}
function drawDiffKeysig(options) {
const f = VexFlowTests.makeFactory(options, 600, 350);
const score = f.EasyScore();
const system = f.System();
const { voice, notes } = createShortcuts(score);
system
.addStave({
voices: [
voice(notes('(d4 e4 g4)/q, c4/q, c4/q/r, c4/q', { stem: 'down' })),
voice(notes('c5/h., c5/q', { stem: 'up' })),
],
})
.addClef('treble')
.addTimeSignature('4/4')
.addKeySignature('D');
system
.addStave({
voices: [voice(notes('c#3/q, cn3/q, bb3/q, d##3/q', { clef: 'bass' }))],
})
.addClef('bass')
.addTimeSignature('4/4');
system.addConnector().setType(StaveConnector.type.BRACKET);
f.draw();
options.assert.expect(0);
}
function drawBasicMutedTest(options) {
const f = VexFlowTests.makeFactory(options, 600, 350);
const score = f.EasyScore();
const system = f.System();
const { voice, notes } = createShortcuts(score);
system
.addStave({
voices: [
voice(notes('(d4 e4 g4)/q/m, c4/q/m, c4/q/r, c4/q/m', { stem: 'down' })),
voice(notes('c#5/h/m., c5/q/m', { stem: 'up' })),
],
})
.addClef('treble');
system
.addStave({
voices: [voice(notes('c#3/q/m, cn3/q/m, bb3/q/m, d##3/q/m', { clef: 'bass' }))],
})
.addClef('bass');
system.addConnector().setType(StaveConnector.type.BRACKET);
f.draw();
options.assert.expect(0);
}
function drawBasicHarmonicTest(options) {
const f = VexFlowTests.makeFactory(options, 600, 350);
const score = f.EasyScore();
const system = f.System();
const { voice, notes } = createShortcuts(score);
system
.addStave({
voices: [
voice(notes('(d4 e4 g4)/q/h, c4/q/h, c4/q/r, c4/q/h', { stem: 'down' })),
voice(notes('c#5/h/h., c5/q/h', { stem: 'up' })),
],
})
.addClef('treble');
system
.addStave({
voices: [voice(notes('c#3/q/h, cn3/q/h, bb3/q/h, d##3/q/h', { clef: 'bass' }))],
})
.addClef('bass');
system.addConnector().setType(StaveConnector.type.BRACKET);
f.draw();
options.assert.expect(0);
}
function drawBasicSlashTest(options) {
const f = VexFlowTests.makeFactory(options, 600, 350);
const score = f.EasyScore();
const system = f.System();
const { voice, notes } = createShortcuts(score);
system
.addStave({
voices: [
voice(notes('(d4 e4 g4)/q/s, c4/q/s, c4/q/r, c4/q/s', { stem: 'down' })),
voice(notes('c#5/h/s., c5/q/s', { stem: 'up' })),
],
})
.addClef('treble');
system
.addStave({
voices: [voice(notes('c#3/q/s, cn3/q/s, bb3/q/s, d##3/q/s', { clef: 'bass' }))],
})
.addClef('bass');
system.addConnector().setType(StaveConnector.type.BRACKET);
f.draw();
options.assert.expect(0);
}
function drawGhostBasicTest(options) {
const f = VexFlowTests.makeFactory(options, 550);
const score = f.EasyScore();
const system = f.System();
system.addStave({
voices: [
score.voice([
...score.notes('f#5/4, f5, db5, c5', { stem: 'up' }),
...score.beam(score.notes('c5/8, d5, fn5, e5', { stem: 'up' })),
...score.beam(score.notes('d5, c5', { stem: 'up' })),
], { time: '7/4' }),
score.voice(score.notes('c4/h/g, f4/4, c4/4/g, e4/4, c4/8/g, d##4/8, c4/8, c4/8', { stem: 'down' }), {
time: '7/4',
}),
],
});
f.draw();
options.assert.expect(0);
}
function drawGhostDottedTest(options) {
const f = VexFlowTests.makeFactory(options, 550);
const score = f.EasyScore();
const system = f.System();
system.addStave({
voices: [
score.voice([
...score.notes('c4/4/g., fbb5/8, d5/4', { stem: 'up' }),
...score.beam(score.notes('c5/8, c#5/16, d5/16', { stem: 'up' })),
...score.notes('c4/2/g.., fn5/8', { stem: 'up' }),
], { time: '8/4' }),
score.voice([
...score.notes('f#4/4', { stem: 'down' }),
...score.beam(score.notes('e4/8, d4/8', { stem: 'down' })),
...score.notes('c4/4/g.., cb4/16, c#4/h, d4/4', { stem: 'down' }),
...score.beam(score.notes('fn4/8, e4/8', { stem: 'down' })),
], { time: '8/4' }),
],
});
f.draw();
options.assert.expect(0);
}
function drawParenthesisedTest(options) {
const f = VexFlowTests.makeFactory(options, 600, 350);
const score = f.EasyScore();
const system = f.System();
const { voice, notes } = createShortcuts(score);
const notes1 = notes('(d4 e4 g4)/q, c4/q, c4/q/r, c4/q', { stem: 'down' });
Parenthesis.buildAndAttach([notes1[0], notes1[3]]);
const notes2 = notes('c#5/h., c5/q', { stem: 'down' });
Parenthesis.buildAndAttach([notes2[0], notes2[1]]);
system
.addStave({
voices: [voice(notes1), voice(notes2)],
})
.addClef('treble');
const notes3 = notes('c#3/q, cn3/q, bb3/q, d##3/q', { stem: 'down' });
Parenthesis.buildAndAttach(notes3);
system
.addStave({
voices: [voice(notes3)],
})
.addClef('bass');
system.addConnector().setType(StaveConnector.type.BRACKET);
f.draw();
options.assert.expect(0);
}
function drawAccidentalsTest(options) {
const f = VexFlowTests.makeFactory(options, 600, 350);
const score = f.EasyScore();
const system = f.System();
const { voice, notes } = createShortcuts(score);
system
.addStave({
voices: [
voice(notes('(cbbs4 ebb4 gbss4)/q, cbs4/q, cdb4/q/r, cd4/q', { stem: 'down' })),
voice(notes('c++-5/h., c++5/q', { stem: 'up' })),
],
})
.addClef('treble');
system
.addStave({
voices: [voice(notes('c+-3/q, c+3/q, bb3/q, d##3/q', { clef: 'bass' }))],
})
.addClef('bass');
system.addConnector().setType(StaveConnector.type.BRACKET);
f.draw();
options.assert.expect(0);
}
function drawBeamsTest(options) {
const f = VexFlowTests.makeFactory(options, 600, 250);
const score = f.EasyScore();
const system = f.System();
const { voice, notes, beam } = createShortcuts(score);
system
.addStave({
voices: [
voice(notes('(c4 e4 g4)/q, c4/q, c4/q/r, c4/q', { stem: 'down' })),
voice([...notes('c#5/h.', { stem: 'up' }), ...beam(notes('c5/8, c5/8', { stem: 'up' }))]),
],
})
.addClef('treble');
f.draw();
options.assert.expect(0);
}
function drawTupletsTest(options) {
const f = VexFlowTests.makeFactory(options, 600, 250);
const score = f.EasyScore();
const system = f.System();
const { voice, notes, tuplet, beam } = createShortcuts(score);
const v1Tuplet = tuplet(notes('(c4 e4 g4)/q, cbb4/q, c4/q', { stem: 'down' }), {
location: Tuplet.LOCATION_BOTTOM,
});
const v1HalfNote = notes('c4/h', { stem: 'down' });
const v1 = voice([...v1Tuplet, ...v1HalfNote]);
const v2HalfNote = notes('c#5/h.', { stem: 'up' });
const v2Tuplet = tuplet(beam(notes('cb5/8, cn5/8, c5/8', { stem: 'up' })));
const v2 = voice([...v2HalfNote, ...v2Tuplet]);
system.addStave({ voices: [v1, v2] }).addClef('treble');
f.draw();
options.assert.expect(0);
}
function drawDotsTest(options) {
const f = VexFlowTests.makeFactory(options, 600, 250);
const score = f.EasyScore();
const system = f.System();
const { voice, notes } = createShortcuts(score);
system
.addStave({
voices: [voice(notes('(c4 e4 g4)/8., (c4 e4 g4)/8.., (c4 e4 g4)/8..., (c4 e4 g4)/8...., (c4 e4 g4)/16...'))],
})
.addClef('treble');
f.draw();
options.assert.expect(0);
}
function drawOptionsTest(options) {
const f = VexFlowTests.makeFactory(options, 500, 200);
const score = f.EasyScore();
const system = f.System();
const notes = score.notes('B4/h[id="foobar", class="red,bold", stem="up", articulations="staccato.below,tenuto"], B4/q[articulations="accent.above"], B4/q[stem="down"]');
system.addStave({ voices: [score.voice(notes)] });
f.draw();
const note0 = notes[0];
const note1 = notes[1];
const note0Modifier0 = note0.getModifiers()[0];
const note0Modifier1 = note0.getModifiers()[1];
const note1Modifier0 = note1.getModifiers()[0];
options.assert.equal(note0.getAttribute('id'), 'foobar');
options.assert.ok(note0.hasClass('red'));
options.assert.ok(note0.hasClass('bold'));
options.assert.equal(note0Modifier0.getCategory(), Articulation.CATEGORY);
options.assert.equal(note0Modifier0.type, 'a.');
options.assert.equal(note0Modifier0.getPosition(), Modifier.Position.BELOW);
options.assert.equal(note0Modifier1.getCategory(), Articulation.CATEGORY);
options.assert.equal(note0Modifier1.type, 'a-');
options.assert.equal(note0Modifier1.getPosition(), Modifier.Position.ABOVE);
options.assert.equal(note0.getStemDirection(), Stem.UP);
options.assert.equal(note1Modifier0.getCategory(), Articulation.CATEGORY);
options.assert.equal(note1Modifier0.type, 'a>');
options.assert.equal(note1Modifier0.getPosition(), Modifier.Position.ABOVE);
options.assert.equal(notes[2].getStemDirection(), Stem.DOWN);
}
function drawFingeringsTest(options) {
const f = VexFlowTests.makeFactory(options, 500, 200);
const score = f.EasyScore();
const system = f.System();
const notes = score.notes('C4/q[fingerings="1"], E4[fingerings="3.above"], G4[fingerings="5.below"], (C4 E4 G4)[fingerings="1,3,5"]');
system.addStave({ voices: [score.voice(notes)] });
f.draw();
const note0Modifier0 = notes[0].getModifiers()[0];
options.assert.equal(note0Modifier0.getCategory(), FretHandFinger.CATEGORY);
options.assert.equal(note0Modifier0.getFretHandFinger(), '1');
options.assert.equal(note0Modifier0.getPosition(), Modifier.Position.LEFT);
const note1Modifier0 = notes[1].getModifiers()[0];
options.assert.equal(note1Modifier0.getCategory(), FretHandFinger.CATEGORY);
options.assert.equal(note1Modifier0.getFretHandFinger(), '3');
options.assert.equal(note1Modifier0.getPosition(), Modifier.Position.ABOVE);
const note2Modifier0 = notes[2].getModifiers()[0];
options.assert.equal(note2Modifier0.getCategory(), FretHandFinger.CATEGORY);
options.assert.equal(note2Modifier0.getFretHandFinger(), '5');
options.assert.equal(note2Modifier0.getPosition(), Modifier.Position.BELOW);
const note3Modifiers = notes[3].getModifiers();
const note3Modifier0 = note3Modifiers[0];
const note3Modifier1 = note3Modifiers[1];
const note3Modifier2 = note3Modifiers[2];
options.assert.equal(note3Modifier0.getCategory(), FretHandFinger.CATEGORY);
options.assert.equal(note3Modifier0.getFretHandFinger(), '1');
options.assert.equal(note3Modifier0.getPosition(), Modifier.Position.LEFT);
options.assert.equal(note3Modifier1.getCategory(), FretHandFinger.CATEGORY);
options.assert.equal(note3Modifier1.getFretHandFinger(), '3');
options.assert.equal(note3Modifier1.getPosition(), Modifier.Position.LEFT);
options.assert.equal(note3Modifier2.getCategory(), FretHandFinger.CATEGORY);
options.assert.equal(note3Modifier2.getFretHandFinger(), '5');
options.assert.equal(note3Modifier2.getPosition(), Modifier.Position.LEFT);
}
function keys(options) {
const f = VexFlowTests.makeFactory(options, 700, 200);
const score = f.EasyScore();
const system = f.System();
const notes = score.notes('c#3/q, c##3, cb3, cbb3, cn3, c3, cbbs3, cbss3, cbs3, cdb3, cd3, c++-3, c++3, c+-3, c+3, co3, ck3', { clef: 'bass' });
system.addStave({ voices: [f.Voice().setStrict(false).addTickables(notes)] }).addClef('bass');
f.draw();
options.assert.equal(notes[0].keys, 'c#/3');
options.assert.equal(notes[1].keys, 'c##/3');
options.assert.equal(notes[2].keys, 'cb/3');
options.assert.equal(notes[3].keys, 'cbb/3');
options.assert.equal(notes[4].keys, 'cn/3');
for (let i = 5; i < notes.length; i++) {
options.assert.equal(notes[i].keys, 'c/3');
}
}
VexFlowTests.register(EasyScoreTests);
export { EasyScoreTests };