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