const test = require('tap').test const sinon = require('sinon') const fs = require('fs') const htmlparser = require('htmlparser2') const QRCode = require('core/qrcode') const SvgRenderer = require('renderer/svg') function getExpectedViewbox (size, margin) { const expectedQrCodeSize = size + margin * 2 return '0 0 ' + expectedQrCodeSize + ' ' + expectedQrCodeSize } function testSvgFragment (t, svgFragment, expectedTags) { return new Promise(function (resolve, reject) { const parser = new htmlparser.Parser({ onopentag: function (name, attribs) { const tag = expectedTags.shift() t.equal(tag.name, name, 'Should have a ' + tag.name + ' tag') tag.attribs.forEach(function (attr) { t.equal(attribs[attr.name], attr.value.toString(), 'Should have attrib ' + attr.name + ' with value ' + attr.value) }) }, onend: function () { resolve() }, onerror: function (e) { reject(e) } }, { decodeEntities: true }) parser.write(svgFragment) parser.end() }) } function buildTest (t, data, opts, expectedTags) { const svg = SvgRenderer.render(data, opts) return testSvgFragment(t, svg, expectedTags.slice()) } test('svgrender interface', function (t) { t.type(SvgRenderer.render, 'function', 'Should have render function') t.type(SvgRenderer.renderToFile, 'function', 'Should have renderToFile function') t.end() }) test('Svg render', function (t) { const tests = [] const data = QRCode.create('sample text', { version: 2 }) const size = data.modules.size tests.push(buildTest(t, data, { scale: 4, margin: 4, color: { light: '#ffffff80' } }, [ { name: 'svg', attribs: [ { name: 'viewbox', value: getExpectedViewbox(size, 4) } ] }, { name: 'path', attribs: [ { name: 'fill', value: '#ffffff' }, { name: 'fill-opacity', value: '.50' } ] }, { name: 'path', attribs: [ { name: 'stroke', value: '#000000' } ] } ])) tests.push(buildTest(t, data, { scale: 0, margin: 8, color: { light: '#0000', dark: '#00000080' } }, [ { name: 'svg', attribs: [ { name: 'viewbox', value: getExpectedViewbox(size, 8) } ] }, { name: 'path', attribs: [ { name: 'stroke', value: '#000000' }, { name: 'stroke-opacity', value: '.50' } ] } ])) tests.push(buildTest(t, data, {}, [ { name: 'svg', attribs: [ { name: 'viewbox', value: getExpectedViewbox(size, 4) } ] }, { name: 'path', attribs: [{ name: 'fill', value: '#ffffff' }] }, { name: 'path', attribs: [{ name: 'stroke', value: '#000000' }] } ])) tests.push(buildTest(t, data, { width: 250 }, [ { name: 'svg', attribs: [ { name: 'width', value: '250' }, { name: 'height', value: '250' }, { name: 'viewbox', value: getExpectedViewbox(size, 4) } ] }, { name: 'path', attribs: [{ name: 'fill', value: '#ffffff' }] }, { name: 'path', attribs: [{ name: 'stroke', value: '#000000' }] } ])) Promise.all(tests).then(function () { t.end() }) }) test('Svg renderToFile', function (t) { const sampleQrData = QRCode.create('sample text', { version: 2 }) const fileName = 'qrimage.svg' let fsStub = sinon.stub(fs, 'writeFile').callsArg(2) t.plan(5) SvgRenderer.renderToFile(fileName, sampleQrData, function (err) { t.ok(!err, 'Should not generate errors with only qrData param') t.equal(fsStub.getCall(0).args[0], fileName, 'Should save file with correct file name') }) SvgRenderer.renderToFile(fileName, sampleQrData, { margin: 10, scale: 1 }, function (err) { t.ok(!err, 'Should not generate errors with options param') t.equal(fsStub.getCall(0).args[0], fileName, 'Should save file with correct file name') }) fsStub.restore() fsStub = sinon.stub(fs, 'writeFile').callsArgWith(2, new Error()) SvgRenderer.renderToFile(fileName, sampleQrData, function (err) { t.ok(err, 'Should fail if error occurs during save') }) fsStub.restore() })