1const REPORT_URL = 'http://localhost:8081/report_gold_data' 2// Set this to enforce that the gold server must be up. 3// Typically used for debugging. 4const fail_on_no_gold = false; 5 6function reportCanvas(canvas, testname, outputType='canvas') { 7 let b64 = canvas.toDataURL('image/png'); 8 return _report(b64, outputType, testname); 9} 10 11function reportSVG(svg, testname) { 12 // This converts an SVG to a base64 encoded PNG. It basically creates an 13 // <img> element that takes the inlined SVG and draws it on a canvas. 14 // The trick is we have to wait until the image is loaded, thus the Promise 15 // wrapping below. 16 let svgStr = svg.outerHTML; 17 let tempImg = document.createElement('img'); 18 19 let tempCanvas = document.createElement('canvas'); 20 let canvasCtx = tempCanvas.getContext('2d'); 21 setCanvasSize(canvasCtx, svg.getAttribute('width'), svg.getAttribute('height')); 22 23 return new Promise(function(resolve, reject) { 24 tempImg.onload = () => { 25 canvasCtx.drawImage(tempImg, 0, 0); 26 let b64 = tempCanvas.toDataURL('image/png'); 27 _report(b64, 'svg', testname).then(() => { 28 resolve(); 29 }).catch((e) => reject(e)); 30 }; 31 tempImg.setAttribute('src', 'data:image/svg+xml;,' + svgStr); 32 }); 33} 34 35// For tests that just do a simple path and return it as a string, wrap it in 36// a proper svg and send it off. Supports fill (nofill means just stroke it). 37// This uses the "standard" size of 600x600. 38function reportSVGString(svgstr, testname, fillRule='nofill') { 39 let newPath = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 40 newPath.setAttribute('stroke', 'black'); 41 if (fillRule !== 'nofill') { 42 newPath.setAttribute('fill', 'orange'); 43 newPath.setAttribute('fill-rule', fillRule); 44 } else { 45 newPath.setAttribute('fill', 'rgba(255,255,255,0.0)'); 46 } 47 newPath.setAttribute('d', svgstr); 48 let newSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 49 newSVG.appendChild(newPath); 50 // helps with the conversion to PNG. 51 newSVG.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); 52 newSVG.setAttribute('width', 600); 53 newSVG.setAttribute('height', 600); 54 return reportSVG(newSVG, testname); 55} 56 57// Reports a canvas and then an SVG of this path. Puts it on a standard size canvas. 58function reportPath(path, testname, done) { 59 let canvas = document.createElement('canvas'); 60 let canvasCtx = canvas.getContext('2d'); 61 // Set canvas size and make it a bit bigger to zoom in on the lines 62 standardizedCanvasSize(canvasCtx); 63 canvasCtx.stroke(path.toPath2D()); 64 65 let svgStr = path.toSVGString(); 66 67 return reportCanvas(canvas, testname).then(() => { 68 reportSVGString(svgStr, testname).then(() => { 69 done(); 70 }).catch(reportError(done)); 71 }).catch(reportError(done)); 72} 73 74// data is a base64 encoded png, outputType is the value that goes with the 75// key 'config' when reporting. 76function _report(data, outputType, testname) { 77 return fetch(REPORT_URL, { 78 method: 'POST', 79 mode: 'no-cors', 80 headers: { 81 'Content-Type': 'application/json', 82 }, 83 body: JSON.stringify({ 84 'output_type': outputType, 85 'data': data, 86 'test_name': testname, 87 }) 88 }).then(() => console.log(`Successfully reported ${testname} to gold aggregator`)); 89} 90 91function reportError(done) { 92 return (e) => { 93 console.log("Error with fetching. Likely could not connect to aggregator server", e.message); 94 if (fail_on_no_gold) { 95 expect(e).toBeUndefined(); 96 } 97 done(); 98 }; 99} 100 101function setCanvasSize(ctx, width, height) { 102 ctx.canvas.width = width; 103 ctx.canvas.height = height; 104} 105 106function standardizedCanvasSize(ctx) { 107 setCanvasSize(ctx, 600, 600); 108} 109 110// A wrapper to catch and print a stacktrace to the logs. 111// Exceptions normally shows up in the browser console, 112// but not in the logs that appear on the bots AND a thrown 113// exception will normally cause a test to time out. 114// This wrapper mitigates both those pain points. 115function catchException(done, fn) { 116 return () => { 117 try { 118 fn() 119 } catch (e) { 120 console.log('Failed with the following error', e); 121 expect(e).toBeFalsy(); 122 debugger; 123 done(); 124 } 125 // We don't call done with finally because 126 // that would make the break the asynchronous nature 127 // of fn(). 128 } 129} 130