const REPORT_URL = 'http://localhost:8081/report_gold_data' // Set this to enforce that the gold server must be up. // Typically used for debugging. const fail_on_no_gold = false; function reportCanvas(canvas, testname, outputType='canvas') { let b64 = canvas.toDataURL('image/png'); return _report(b64, outputType, testname); } function reportSVG(svg, testname) { // This converts an SVG to a base64 encoded PNG. It basically creates an // element that takes the inlined SVG and draws it on a canvas. // The trick is we have to wait until the image is loaded, thus the Promise // wrapping below. let svgStr = svg.outerHTML; let tempImg = document.createElement('img'); let tempCanvas = document.createElement('canvas'); let canvasCtx = tempCanvas.getContext('2d'); setCanvasSize(canvasCtx, svg.getAttribute('width'), svg.getAttribute('height')); return new Promise(function(resolve, reject) { tempImg.onload = () => { canvasCtx.drawImage(tempImg, 0, 0); let b64 = tempCanvas.toDataURL('image/png'); _report(b64, 'svg', testname).then(() => { resolve(); }).catch((e) => reject(e)); }; tempImg.setAttribute('src', 'data:image/svg+xml;,' + svgStr); }); } // For tests that just do a simple path and return it as a string, wrap it in // a proper svg and send it off. Supports fill (nofill means just stroke it). // This uses the "standard" size of 600x600. function reportSVGString(svgstr, testname, fillRule='nofill') { let newPath = document.createElementNS('http://www.w3.org/2000/svg', 'path'); newPath.setAttribute('stroke', 'black'); if (fillRule !== 'nofill') { newPath.setAttribute('fill', 'orange'); newPath.setAttribute('fill-rule', fillRule); } else { newPath.setAttribute('fill', 'rgba(255,255,255,0.0)'); } newPath.setAttribute('d', svgstr); let newSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); newSVG.appendChild(newPath); // helps with the conversion to PNG. newSVG.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); newSVG.setAttribute('width', 600); newSVG.setAttribute('height', 600); return reportSVG(newSVG, testname); } // Reports a canvas and then an SVG of this path. Puts it on a standard size canvas. function reportPath(path, testname, done) { let canvas = document.createElement('canvas'); let canvasCtx = canvas.getContext('2d'); // Set canvas size and make it a bit bigger to zoom in on the lines standardizedCanvasSize(canvasCtx); canvasCtx.stroke(path.toPath2D()); let svgStr = path.toSVGString(); return reportCanvas(canvas, testname).then(() => { reportSVGString(svgStr, testname).then(() => { done(); }).catch(reportError(done)); }).catch(reportError(done)); } // data is a base64 encoded png, outputType is the value that goes with the // key 'config' when reporting. function _report(data, outputType, testname) { return fetch(REPORT_URL, { method: 'POST', mode: 'no-cors', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ 'output_type': outputType, 'data': data, 'test_name': testname, }) }).then(() => console.log(`Successfully reported ${testname} to gold aggregator`)); } function reportError(done) { return (e) => { console.log("Error with fetching. Likely could not connect to aggregator server", e.message); if (fail_on_no_gold) { expect(e).toBeUndefined(); } done(); }; } function setCanvasSize(ctx, width, height) { ctx.canvas.width = width; ctx.canvas.height = height; } function standardizedCanvasSize(ctx) { setCanvasSize(ctx, 600, 600); } // A wrapper to catch and print a stacktrace to the logs. // Exceptions normally shows up in the browser console, // but not in the logs that appear on the bots AND a thrown // exception will normally cause a test to time out. // This wrapper mitigates both those pain points. function catchException(done, fn) { return () => { try { fn() } catch (e) { console.log('Failed with the following error', e); expect(e).toBeFalsy(); debugger; done(); } // We don't call done with finally because // that would make the break the asynchronous nature // of fn(). } }