Home | History | Annotate | Download | only in tests
      1 jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;
      2 
      3 describe('PathKit\'s Path2D API', function() {
      4     // Note, don't try to print the PathKit object - it can cause Karma/Jasmine to lock up.
      5     var PathKit = null;
      6     const LoadPathKit = new Promise(function(resolve, reject) {
      7         if (PathKit) {
      8             resolve();
      9         } else {
     10             PathKitInit({
     11                 locateFile: (file) => '/pathkit/'+file,
     12             }).ready().then((_PathKit) => {
     13                 PathKit = _PathKit;
     14                 resolve();
     15             });
     16         }
     17     });
     18 
     19     it('can do everything in the Path2D API w/o crashing', function(done) {
     20         LoadPathKit.then(catchException(done, () => {
     21             // This is taken from example.html
     22             let path = PathKit.NewPath();
     23 
     24             path.moveTo(20, 5);
     25             path.lineTo(30, 20);
     26             path.lineTo(40, 10);
     27             path.lineTo(50, 20);
     28             path.lineTo(60, 0);
     29             path.lineTo(20, 5);
     30 
     31             path.moveTo(20, 80);
     32             path.bezierCurveTo(90, 10, 160, 150, 190, 10);
     33 
     34             path.moveTo(36, 148);
     35             path.quadraticCurveTo(66, 188, 120, 136);
     36             path.lineTo(36, 148);
     37 
     38             path.rect(5, 170, 20, 20);
     39 
     40             path.moveTo(150, 180);
     41             path.arcTo(150, 100, 50, 200, 20);
     42             path.lineTo(160, 160);
     43 
     44             path.moveTo(20, 120);
     45             path.arc(20, 120, 18, 0, 1.75 * Math.PI);
     46             path.lineTo(20, 120);
     47 
     48             let secondPath = PathKit.NewPath();
     49             secondPath.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI, false);
     50 
     51             path.addPath(secondPath);
     52 
     53             let m = document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGMatrix();
     54             m.a = 1; m.b = 0;
     55             m.c = 0; m.d = 1;
     56             m.e = 0; m.f = 20.5;
     57 
     58             path.addPath(secondPath, m);
     59 
     60             let canvas = document.createElement('canvas');
     61             let canvasCtx = canvas.getContext('2d');
     62             // Set canvas size and make it a bit bigger to zoom in on the lines
     63             standardizedCanvasSize(canvasCtx);
     64             canvasCtx.scale(3.0, 3.0);
     65             canvasCtx.fillStyle = 'blue';
     66             canvasCtx.stroke(path.toPath2D());
     67 
     68             let svgPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
     69             svgPath.setAttribute('stroke', 'black');
     70             svgPath.setAttribute('fill', 'rgba(255,255,255,0.0)');
     71             svgPath.setAttribute('transform', 'scale(3.0, 3.0)');
     72             svgPath.setAttribute('d', path.toSVGString());
     73 
     74             let newSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
     75             newSVG.appendChild(svgPath);
     76             newSVG.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
     77             newSVG.setAttribute('width', 600);
     78             newSVG.setAttribute('height', 600);
     79 
     80             path.delete();
     81             secondPath.delete();
     82 
     83             reportCanvas(canvas, 'path2D_api_example').then(() => {
     84                 reportSVG(newSVG, 'path2D_api_example').then(() => {
     85                     done();
     86                 }).catch(reportError(done));
     87             }).catch(reportError(done));
     88         }));
     89     });
     90 
     91     it('can chain by returning the same object', function(done) {
     92         LoadPathKit.then(catchException(done, () => {
     93             let path = PathKit.NewPath();
     94 
     95             let p1 = path.moveTo(20, 5)
     96                 .lineTo(30, 20)
     97                 .quadTo(66, 188, 120, 136)
     98                 .close();
     99 
    100             // these should be the same object
    101             expect(path === p1).toBe(true);
    102             p1.delete();
    103             try {
    104                 // This should throw an exception because
    105                 // the underlying path was already deleted.
    106                 path.delete();
    107                 expect('should not have gotten here').toBe(false);
    108             } catch (e) {
    109                 // all is well
    110             }
    111             done();
    112         }));
    113     });
    114 
    115     it('does not leak path objects when chaining', function(done) {
    116         LoadPathKit.then(catchException(done, () => {
    117             // By default, we have 16 MB of memory assigned to our PathKit
    118             // library. This can be configured by -S TOTAL_MEMORY=NN
    119             // and defaults to 16MB (we likely don't need to touch this).
    120             // If there's a leak in here, we should OOM pretty quick.
    121             // Testing showed around 50k is enough to see one if we leak a path,
    122             // so run 250k times just to be safe.
    123             for(let i = 0; i < 250000; i++) {
    124                 let path = PathKit.NewPath()
    125                                   .moveTo(20, 5)
    126                                   .lineTo(30, 20)
    127                                   .quadTo(66, 188, 120, 136)
    128                                   .close();
    129                 path.delete();
    130             }
    131             done();
    132         }));
    133     });
    134 
    135     function drawTriangle() {
    136         let path = PathKit.NewPath();
    137         path.moveTo(0, 0);
    138         path.lineTo(10, 0);
    139         path.lineTo(10, 10);
    140         path.close();
    141         return path;
    142     }
    143 
    144     it('has multiple overloads of addPath', function(done) {
    145         LoadPathKit.then(catchException(done, () => {
    146             let basePath = PathKit.NewPath();
    147             let otherPath = drawTriangle();
    148             // These add path call can be chained.
    149             // add it unchanged
    150             basePath.addPath(otherPath)
    151             // providing the 6 params of an SVG matrix to make it appear 20.5 px down
    152                     .addPath(otherPath, 1, 0, 0, 1, 0, 20.5)
    153             // provide the full 9 matrix params to make it appear 30 px to the right
    154             // and be 3 times as big.
    155                     .addPath(otherPath, 3, 0, 30,
    156                                         0, 3, 0,
    157                                         0, 0, 1);
    158 
    159             reportPath(basePath, 'add_path_3x', done);
    160             basePath.delete();
    161             otherPath.delete();
    162         }));
    163     });
    164 
    165     it('approximates arcs (conics) with quads', function(done) {
    166         LoadPathKit.then(catchException(done, () => {
    167             let path = PathKit.NewPath();
    168             path.moveTo(50, 120);
    169             path.arc(50, 120, 45, 0, 1.75 * Math.PI);
    170             path.lineTo(50, 120);
    171 
    172             let canvas = document.createElement('canvas');
    173             let canvasCtx = canvas.getContext('2d');
    174             standardizedCanvasSize(canvasCtx);
    175             // The and.callThrough is important to make it actually
    176             // draw the quadratics
    177             spyOn(canvasCtx, 'quadraticCurveTo').and.callThrough();
    178 
    179             canvasCtx.beginPath();
    180             path.toCanvas(canvasCtx);
    181             canvasCtx.stroke();
    182             // No need to check the whole path, as that's more what the
    183             // gold correctness tests are for (can account for changes we make
    184             // to the approximation algorithms).
    185             expect(canvasCtx.quadraticCurveTo).toHaveBeenCalled();
    186             path.delete();
    187             reportCanvas(canvas, 'conics_quads_approx').then(() => {
    188                 done();
    189             }).catch(reportError(done));
    190         }));
    191     });
    192 
    193 });
    194