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