UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature.

312 lines (311 loc) 10.6 kB
import { Factory, Renderer, VexFlow } from '../src/index.js'; import { Metrics } from '../src/metrics.js'; import { globalObject } from '../src/util.js'; const global = globalObject(); let originalFontNames; function useTempFontStack(fontName) { originalFontNames = VexFlow.getFonts(); VexFlow.setFonts(...VexFlowTests.FONT_STACKS[fontName]); } function restoreOriginalFontStack() { VexFlow.setFonts(...originalFontNames); } if (!global.$) { global.$ = (param) => { let element; if (typeof param !== 'string') { element = param; } else if (param.startsWith('<')) { const tagName = param.match(/[A-Za-z]+/g)[0]; element = document.createElement(tagName); } else { element = document.querySelector(param); } const $element = { get(index) { return element; }, addClass(c) { element.classList.add(c); return $element; }, text(t) { element.textContent = t; return $element; }, html(h) { if (!h) { return element.innerHTML; } else { element.innerHTML = h; return $element; } }, append(...elementsToAppend) { elementsToAppend.forEach((e) => { element.appendChild(e); }); return $element; }, attr(attrName, val) { element.setAttribute(attrName, val); return $element; }, }; return $element; }; } function sanitize(text) { return text.replace(/[^a-zA-Z0-9]/g, '_'); } const CANVAS_TEST_CONFIG = { backend: Renderer.Backends.CANVAS, tagName: 'canvas', testType: 'Canvas', fontStacks: ['Bravura'], }; const CANVAS_TEXT_CONFIG = { backend: Renderer.Backends.CANVAS, tagName: 'canvas', testType: 'Canvas', fontStacks: ['Bravura'], }; const SVG_TEST_CONFIG = { backend: Renderer.Backends.SVG, tagName: 'div', testType: 'SVG', fontStacks: [ 'Bravura', 'Gonville', 'Petaluma', ], }; const SVG_TEXT_CONFIG = { backend: Renderer.Backends.SVG, tagName: 'div', testType: 'SVG', fontStacks: ['Bravura'], }; const NODE_TEST_CONFIG = { backend: Renderer.Backends.CANVAS, tagName: 'canvas', testType: 'NodeCanvas', fontStacks: ['Bravura', 'Petaluma', 'Leland', 'Gonville'], }; export class VexFlowTests { static register(test) { VexFlowTests.tests.push(test); } static parseJobOptions(runOptions) { let { jobs, job } = runOptions || { jobs: 1, job: 0 }; if (window) { const { location } = window; if (location) { const sps = new URLSearchParams(location.search); const jobsParam = sps.get('jobs'); const jobParam = sps.get('job'); if (jobsParam) { jobs = parseInt(jobsParam, 10); } if (jobParam) { job = parseInt(jobParam, 10); } } } return { jobs, job, }; } static run(runOptions) { const { jobs, job } = VexFlowTests.parseJobOptions(runOptions); VexFlowTests.tests.forEach((test, idx) => { if (jobs === 1 || idx % jobs === job) { test.Start(); } }); } static set NODE_FONT_STACKS(fontStacks) { NODE_TEST_CONFIG.fontStacks = fontStacks; } static generateTestID(prefix) { return prefix + '_' + VexFlowTests.NEXT_TEST_ID++; } static runTests(name, testFunc, params) { VexFlowTests.runCanvasTest(name, testFunc, params); VexFlowTests.runSVGTest(name, testFunc, params); VexFlowTests.runNodeTest(name, testFunc, params); } static runTextTests(name, testFunc, params) { VexFlowTests.runCanvasText(name, testFunc, params); VexFlowTests.runSVGText(name, testFunc, params); } static createTest(elementId, testTitle, tagName, titleId = '') { const anchorTestTitle = `<a href="#${titleId}">${testTitle}</a>`; const title = $('<div/>').addClass('name').attr('id', titleId).html(anchorTestTitle).get(0); const vexOutput = $(`<${tagName}/>`).addClass('vex-tabdiv').attr('id', elementId).get(0); const container = $('<div/>').addClass('testcanvas').append(title, vexOutput).get(0); $('#qunit-tests').append(container); return vexOutput; } static makeFactory(options, width = 450, height = 140) { const { elementId, backend } = options; return new Factory({ renderer: { elementId, backend, width, height } }); } static runCanvasTest(name, testFunc, params) { if (VexFlowTests.RUN_CANVAS_TESTS) { const helper = null; VexFlowTests.runWithParams(Object.assign(Object.assign({}, CANVAS_TEST_CONFIG), { name, testFunc, params, helper })); } } static runCanvasText(name, testFunc, params) { if (VexFlowTests.RUN_CANVAS_TESTS) { const helper = null; VexFlowTests.runWithParams(Object.assign(Object.assign({}, CANVAS_TEXT_CONFIG), { name, testFunc, params, helper })); } } static runSVGTest(name, testFunc, params) { if (VexFlowTests.RUN_SVG_TESTS) { const helper = null; VexFlowTests.runWithParams(Object.assign(Object.assign({}, SVG_TEST_CONFIG), { name, testFunc, params, helper })); } } static runSVGText(name, testFunc, params) { if (VexFlowTests.RUN_SVG_TESTS) { const helper = null; VexFlowTests.runWithParams(Object.assign(Object.assign({}, SVG_TEXT_CONFIG), { name, testFunc, params, helper })); } } static runNodeTest(name, testFunc, params) { if (VexFlowTests.RUN_NODE_TESTS) { const helper = VexFlowTests.runNodeTestHelper; VexFlowTests.runWithParams(Object.assign(Object.assign({}, NODE_TEST_CONFIG), { name, testFunc, params, helper })); } } static runNodeTestHelper(fontName, element) { if (Renderer.lastContext !== undefined) { const fileName = VexFlowTests.NODE_IMAGEDIR + '/' + sanitize(QUnit.moduleName) + '.' + sanitize(QUnit.testName) + '.' + sanitize(fontName) + '.jsdom.png'; const imageData = element.toDataURL().split(';base64,').pop(); const imageBuffer = Buffer.from(imageData, 'base64'); VexFlowTests.shims.fs.writeFileSync(fileName, imageBuffer, { encoding: 'base64' }); } } static runWithParams({ fontStacks, testFunc, name, params, backend, tagName, testType, helper }) { if (name === undefined) { throw new Error('Test name is undefined.'); } const testTypeLowerCase = testType.toLowerCase(); fontStacks.forEach((fontStackName) => { QUnit.test(name, (assert) => { useTempFontStack(fontStackName); const sanitizedFontStackName = sanitize(fontStackName); const elementId = VexFlowTests.generateTestID(`${testTypeLowerCase}_` + sanitizedFontStackName); const moduleName = assert.test.module.name; const title = moduleName + ' › ' + name + ` › ${testType} + ${fontStackName}`; const prefix = testTypeLowerCase + '_'; const titleId = `${prefix}${sanitize(moduleName)}.${sanitize(name)}.${sanitizedFontStackName}`; const element = VexFlowTests.createTest(elementId, title, tagName, titleId); const options = { elementId, params, assert, backend }; const isSVG = backend === Renderer.Backends.SVG; const contextBuilder = isSVG ? Renderer.getSVGContext : Renderer.getCanvasContext; testFunc(options, contextBuilder); restoreOriginalFontStack(); if (helper) helper(fontStackName, element); }); }); } static plotLegendForNoteWidth(ctx, x, y) { ctx.save(); ctx.setFont(Metrics.get('fontFamily'), 8); const spacing = 12; let lastY = y; function legend(color, text) { ctx.beginPath(); ctx.setStrokeStyle(color); ctx.setFillStyle(color); ctx.setLineWidth(10); ctx.moveTo(x, lastY - 4); ctx.lineTo(x + 10, lastY - 4); ctx.stroke(); ctx.setFillStyle('black'); ctx.fillText(text, x + 15, lastY); lastY += spacing; } legend('green', 'Note + Flag'); legend('red', 'Modifiers'); legend('#999', 'Displaced Head'); legend('#DDD', 'Formatter Shift'); ctx.restore(); } static drawBoundingBox(ctx, el) { const bb = el.getBoundingBox(); ctx.beginPath(); ctx.rect(bb.getX(), bb.getY(), bb.getW(), bb.getH()); ctx.stroke(); } } VexFlowTests.tests = []; VexFlowTests.RUN_CANVAS_TESTS = true; VexFlowTests.RUN_SVG_TESTS = true; VexFlowTests.RUN_NODE_TESTS = false; VexFlowTests.Font = { size: 10 }; VexFlowTests.FONT_STACKS = { Bravura: ['Bravura', 'Academico'], 'Finale Ash': ['Finale Ash', 'Finale Ash Text'], 'Finale Broadway': ['Finale Broadway', 'Finale Broadway Text'], 'Finale Maestro': ['Finale Maestro', 'Finale Maestro Text'], Gonville: ['Gonville', 'Academico'], Gootville: ['Gootville', 'Edwin'], Leland: ['Leland', 'Edwin'], Leipzig: ['Leipzig', 'Academico'], MuseJazz: ['MuseJazz', 'MuseJazz Text'], Petaluma: ['Petaluma', 'Petaluma Script'], Sebastian: ['Sebastian', 'Nepomuk'], }; VexFlowTests.NEXT_TEST_ID = 0; export const concat = (a, b) => a.concat(b); export const MAJOR_KEYS = [ 'C', 'F', 'Bb', 'Eb', 'Ab', 'Db', 'Gb', 'Cb', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#', ]; export const MINOR_KEYS = [ 'Am', 'Dm', 'Gm', 'Cm', 'Fm', 'Bbm', 'Ebm', 'Abm', 'Em', 'Bm', 'F#m', 'C#m', 'G#m', 'D#m', 'A#m', ];