text: /* eslint-env mocha */
  
  'use strict'
  
  /**
   * Module dependencies.
   */
  
  const { createCanvas, loadImage, rsvgVersion } = require('../');
  const Image = require('../').Image
  const HAVE_SVG = rsvgVersion !== undefined;
  
  const assert = require('assert')
  const assertRejects = require('assert-rejects')
  const fs = require('fs')
  const path = require('path')
  
  const png_checkers = `${__dirname}/fixtures/checkers.png`
  const png_clock = `${__dirname}/fixtures/clock.png`
  const jpg_chrome = `${__dirname}/fixtures/chrome.jpg`
  const jpg_face = `${__dirname}/fixtures/face.jpeg`
  const svg_tree = `${__dirname}/fixtures/tree.svg`
  const bmp_dir = `${__dirname}/fixtures/bmp`
  
  describe('Image', function () {
    it('Prototype and ctor are well-shaped, don\'t hit asserts on accessors (GH-803)', function () {
      var img = new Image()
      assert.throws(function () { Image.prototype.width }, /incompatible receiver/)
      assert(!img.hasOwnProperty('width'))
      assert('width' in img)
      assert(Image.prototype.hasOwnProperty('width'))
    })
  
    it('loads JPEG image', function () {
      return loadImage(jpg_face).then((img) => {
        assert.strictEqual(img.onerror, null)
        assert.strictEqual(img.onload, null)
  
        assert.strictEqual(img.src, jpg_face)
        assert.strictEqual(img.width, 485)
        assert.strictEqual(img.height, 401)
        assert.strictEqual(img.complete, true)
      })
    })
  
    it('loads JPEG data URL', function () {
      const base64Encoded = fs.readFileSync(jpg_face, 'base64')
      const dataURL = `data:image/png;base64,${base64Encoded}`
  
      return loadImage(dataURL).then((img) => {
        assert.strictEqual(img.onerror, null)
        assert.strictEqual(img.onload, null)
  
        assert.strictEqual(img.src, dataURL)
        assert.strictEqual(img.width, 485)
        assert.strictEqual(img.height, 401)
        assert.strictEqual(img.complete, true)
      })
    })
  
    it('loads PNG image', function () {
      return loadImage(png_clock).then((img) => {
        assert.strictEqual(img.onerror, null)
        assert.strictEqual(img.onload, null)
  
        assert.strictEqual(img.src, png_clock)
        assert.strictEqual(img.width, 320)
        assert.strictEqual(img.height, 320)
        assert.strictEqual(img.complete, true)
      })
    })
  
    it('loads PNG data URL', function () {
      const base64Encoded = fs.readFileSync(png_clock, 'base64')
      const dataURL = `data:image/png;base64,${base64Encoded}`
  
      return loadImage(dataURL).then((img) => {
        assert.strictEqual(img.onerror, null)
        assert.strictEqual(img.onload, null)
  
        assert.strictEqual(img.src, dataURL)
        assert.strictEqual(img.width, 320)
        assert.strictEqual(img.height, 320)
        assert.strictEqual(img.complete, true)
      })
    })
  
    it('detects invalid PNG', function (done) {
      if (process.platform === 'win32') this.skip(); // TODO
      const img = new Image()
      img.onerror = () => {
        assert.strictEqual(img.complete, true)
        done()
      }
      img.src = Buffer.from('89504E470D', 'hex')
    })
  
    it('loads SVG data URL base64', function () {
      if (!HAVE_SVG) this.skip();
      const base64Enc = fs.readFileSync(svg_tree, 'base64')
      const dataURL = `data:image/svg+xml;base64,${base64Enc}`
      return loadImage(dataURL).then((img) => {
        assert.strictEqual(img.onerror, null)
        assert.strictEqual(img.onload, null)
        assert.strictEqual(img.width, 200)
        assert.strictEqual(img.height, 200)
        assert.strictEqual(img.complete, true)
      })
    })
  
    it('loads SVG data URL utf8', function () {
      if (!HAVE_SVG) this.skip();
      const utf8Encoded = fs.readFileSync(svg_tree, 'utf8')
      const dataURL = `data:image/svg+xml;utf8,${utf8Encoded}`
      return loadImage(dataURL).then((img) => {
        assert.strictEqual(img.onerror, null)
        assert.strictEqual(img.onload, null)
        assert.strictEqual(img.width, 200)
        assert.strictEqual(img.height, 200)
        assert.strictEqual(img.complete, true)
      })
    })
  
    it('calls Image#onload multiple times', function () {
      return loadImage(png_clock).then((img) => {
        let onloadCalled = 0
  
        img.onload = () => { onloadCalled += 1 }
  
        img.src = png_checkers
        assert.strictEqual(img.src, png_checkers)
        assert.strictEqual(img.complete, true)
        assert.strictEqual(img.width, 2)
        assert.strictEqual(img.height, 2)
  
        img.src = png_clock
        assert.strictEqual(img.src, png_clock)
        assert.strictEqual(true, img.complete)
        assert.strictEqual(320, img.width)
        assert.strictEqual(320, img.height)
  
        assert.strictEqual(onloadCalled, 2)
  
        onloadCalled = 0
        img.onload = () => { onloadCalled += 1 }
  
        img.src = png_clock
        assert.strictEqual(onloadCalled, 1)
      })
    })
  
    it('handles errors', function () {
      return assertRejects(loadImage(`${png_clock}fail`), Error)
    })
  
    it('returns a nice, coded error for fopen failures', function (done) {
      const img = new Image()
      img.onerror = err => {
        assert.equal(err.code, 'ENOENT')
        assert.equal(err.path, 'path/to/nothing')
        assert.equal(err.syscall, 'fopen')
        assert.strictEqual(img.complete, true)
        done()
      }
      img.src = 'path/to/nothing'
    })
  
    it('captures errors from libjpeg', function (done) {
      const img = new Image()
      img.onerror = err => {
        assert.equal(err.message, 'JPEG datastream contains no image')
        assert.strictEqual(img.complete, true)
        done()
      }
      img.src = `${__dirname}/fixtures/159-crash1.jpg`
    })
  
    it('calls Image#onerror multiple times', function () {
      return loadImage(png_clock).then((img) => {
        let onloadCalled = 0
        let onerrorCalled = 0
  
        img.onload = () => { onloadCalled += 1 }
        img.onerror = () => { onerrorCalled += 1 }
  
        img.src = `${png_clock}s1`
        assert.strictEqual(img.src, `${png_clock}s1`)
  
        img.src = `${png_clock}s2`
        assert.strictEqual(img.src, `${png_clock}s2`)
  
        assert.strictEqual(onerrorCalled, 2)
  
        onerrorCalled = 0
        img.onerror = () => { onerrorCalled += 1 }
  
        img.src = `${png_clock}s3`
        assert.strictEqual(img.src, `${png_clock}s3`)
  
        assert.strictEqual(onerrorCalled, 1)
        assert.strictEqual(onloadCalled, 0)
      })
    })
  
    it('Image#{width,height}', function () {
      return loadImage(png_clock).then((img) => {
        img.src = ''
        assert.strictEqual(img.width, 0)
        assert.strictEqual(img.height, 0)
  
        img.src = png_clock
        assert.strictEqual(img.width, 320)
        assert.strictEqual(img.height, 320)
      })
    })
  
    it('Image#src set empty buffer', function () {
      return loadImage(png_clock).then((img) => {
        let onerrorCalled = 0
  
        img.onerror = () => { onerrorCalled += 1 }
  
        img.src = Buffer.alloc(0)
        assert.strictEqual(img.width, 0)
        assert.strictEqual(img.height, 0)
        assert.strictEqual(img.complete, true)
  
        assert.strictEqual(onerrorCalled, 1)
      })
    })
  
    it('should unbind Image#onload', function () {
      return loadImage(png_clock).then((img) => {
        let onloadCalled = 0
  
        img.onload = () => { onloadCalled += 1 }
  
        img.src = png_checkers
        assert.strictEqual(img.src, png_checkers)
        assert.strictEqual(img.complete, true)
        assert.strictEqual(img.width, 2)
        assert.strictEqual(img.height, 2)
  
        assert.strictEqual(onloadCalled, 1)
  
        onloadCalled = 0
        img.onload = null
  
        img.src = png_clock
        assert.strictEqual(img.src, png_clock)
        assert.strictEqual(img.complete, true)
        assert.strictEqual(img.width, 320)
        assert.strictEqual(img.height, 320)
  
        assert.strictEqual(onloadCalled, 0)
      })
    })
  
    it('should unbind Image#onerror', function () {
      return loadImage(png_clock).then((img) => {
        let onloadCalled = 0
        let onerrorCalled = 0
  
        img.onload = () => { onloadCalled += 1 }
        img.onerror = () => { onerrorCalled += 1 }
  
        img.src = `${png_clock}s1`
        assert.strictEqual(img.src, `${png_clock}s1`)
  
        img.src = `${png_clock}s2`
        assert.strictEqual(img.src, `${png_clock}s2`)
  
        assert.strictEqual(onerrorCalled, 2)
  
        onerrorCalled = 0
        img.onerror = null
  
        img.src = `${png_clock}s3`
        assert.strictEqual(img.src, `${png_clock}s3`)
  
        assert.strictEqual(onloadCalled, 0)
        assert.strictEqual(onerrorCalled, 0)
      })
    })
  
    it('does not crash on invalid images', function () {
      function withIncreasedByte (source, index) {
        const copy = source.slice(0)
  
        copy[index] += 1
  
        return copy
      }
  
      const source = fs.readFileSync(jpg_chrome)
  
      const corruptSources = [
        withIncreasedByte(source, 0),
        withIncreasedByte(source, 1),
        withIncreasedByte(source, 1060),
        withIncreasedByte(source, 1061),
        withIncreasedByte(source, 1062),
        withIncreasedByte(source, 1063),
        withIncreasedByte(source, 1064),
        withIncreasedByte(source, 1065),
        withIncreasedByte(source, 1066),
        withIncreasedByte(source, 1067),
        withIncreasedByte(source, 1068),
        withIncreasedByte(source, 1069)
      ]
  
      return Promise.all(corruptSources.map(src => loadImage(src).catch(() => null)))
    })
  
    it('does not contain `source` property', function () {
      var keys = Reflect.ownKeys(Image.prototype)
      assert.ok(!keys.includes('source'))
      assert.ok(!keys.includes('getSource'))
      assert.ok(!keys.includes('setSource'))
    })
  
    describe('supports BMP', function () {
      it('parses 1-bit image', function (done) {
        let img = new Image()
  
        img.onload = () => {
          assert.strictEqual(img.width, 111)
          assert.strictEqual(img.height, 72)
          done()
        }
  
        img.onerror = err => { throw err }
        img.src = path.join(bmp_dir, '1-bit.bmp')
      })
  
      it('parses 4-bit image', function (done) {
        let img = new Image();
  
        img.onload = () => {
          assert.strictEqual(img.width, 32);
          assert.strictEqual(img.height, 32);
          done();
        };
  
        img.onerror = err => { throw err; };
        img.src = path.join(bmp_dir, '4-bit.bmp');
      });
  
      it('parses 8-bit image');
  
      it('parses 24-bit image', function (done) {
        let img = new Image()
  
        img.onload = () => {
          assert.strictEqual(img.width, 2)
          assert.strictEqual(img.height, 2)
  
          testImgd(img, [
            0, 0, 255, 255,
            0, 255, 0, 255,
            255, 0, 0, 255,
            255, 255, 255, 255
          ])
  
          done()
        }
  
        img.onerror = err => { throw err }
        img.src = path.join(bmp_dir, '24-bit.bmp')
      })
  
      it('parses 32-bit image', function (done) {
        let img = new Image()
  
        img.onload = () => {
          assert.strictEqual(img.width, 4)
          assert.strictEqual(img.height, 2)
  
          testImgd(img, [
            0, 0, 255, 255,
            0, 255, 0, 255,
            255, 0, 0, 255,
            255, 255, 255, 255,
            0, 0, 255, 127,
            0, 255, 0, 127,
            255, 0, 0, 127,
            255, 255, 255, 127
          ])
  
          done()
        }
  
        img.onerror = err => { throw err }
        img.src = fs.readFileSync(path.join(bmp_dir, '32-bit.bmp')) // Also tests loading from buffer
      })
  
      it('parses minimal BMP', function (done) {
        let img = new Image()
  
        img.onload = () => {
          assert.strictEqual(img.width, 1)
          assert.strictEqual(img.height, 1)
  
          testImgd(img, [
            255, 0, 0, 255
          ])
  
          done()
        }
  
        img.onerror = err => { throw err }
        img.src = path.join(bmp_dir, 'min.bmp')
      })
  
      it('properly handles negative height', function (done) {
        let img = new Image()
  
        img.onload = () => {
          assert.strictEqual(img.width, 1)
          assert.strictEqual(img.height, 2)
  
          testImgd(img, [
            255, 0, 0, 255,
            0, 255, 0, 255
          ])
  
          done()
        }
  
        img.onerror = err => { throw err }
        img.src = path.join(bmp_dir, 'negative-height.bmp')
      })
  
      it('color palette', function (done) {
        let img = new Image();
  
        img.onload = () => {
          assert.strictEqual(img.width, 32);
          assert.strictEqual(img.height, 32);
          done();
        };
  
        img.onerror = err => { throw err; };
        img.src = path.join(bmp_dir, 'palette.bmp');
      });
  
      it('V3 header', function (done) {
        let img = new Image();
  
        img.onload = () => {
          assert.strictEqual(img.width, 256);
          assert.strictEqual(img.height, 192);
          done();
        };
  
        img.onerror = err => { throw err; };
        img.src = path.join(bmp_dir, 'v3-header.bmp');
      });
  
      it('V5 header');
  
      it('catches BMP errors', function (done) {
        let img = new Image()
  
        img.onload = () => {
          throw new Error('Invalid image should not be loaded properly')
        }
  
        img.onerror = err => {
          let msg = 'Error while processing file header - unexpected end of file'
          assert.strictEqual(err.message, msg)
          done()
        }
  
        img.src = Buffer.from('BM')
      })
  
      it('BMP bomb', function (done) {
        let img = new Image()
  
        img.onload = () => {
          throw new Error('Invalid image should not be loaded properly')
        }
  
        img.onerror = err => {
          done()
        }
  
        img.src = path.join(bmp_dir, 'bomb.bmp')
      })
  
      function testImgd (img, data) {
        let ctx = createCanvas(img.width, img.height).getContext('2d')
        ctx.drawImage(img, 0, 0)
        var actualData = ctx.getImageData(0, 0, img.width, img.height).data
        assert.strictEqual(String(actualData), String(data))
      }
    })
  })
  