api-native-image-spec.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. import { expect } from 'chai';
  2. import { nativeImage } from 'electron/common';
  3. import { ifdescribe, ifit } from './lib/spec-helpers';
  4. import * as path from 'node:path';
  5. describe('nativeImage module', () => {
  6. const fixturesPath = path.join(__dirname, 'fixtures');
  7. const imageLogo = {
  8. path: path.join(fixturesPath, 'assets', 'logo.png'),
  9. width: 538,
  10. height: 190
  11. };
  12. const image1x1 = {
  13. dataUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYlWNgAAIAAAUAAdafFs0AAAAASUVORK5CYII=',
  14. path: path.join(fixturesPath, 'assets', '1x1.png'),
  15. height: 1,
  16. width: 1
  17. };
  18. const image2x2 = {
  19. dataUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQYlWP8//8/AwMDEwMDAwMDAwAkBgMBBMzldwAAAABJRU5ErkJggg==',
  20. path: path.join(fixturesPath, 'assets', '2x2.jpg'),
  21. height: 2,
  22. width: 2
  23. };
  24. const image3x3 = {
  25. dataUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAADElEQVQYlWNgIAoAAAAnAAGZWEMnAAAAAElFTkSuQmCC',
  26. path: path.join(fixturesPath, 'assets', '3x3.png'),
  27. height: 3,
  28. width: 3
  29. };
  30. const dataUrlImages = [
  31. image1x1,
  32. image2x2,
  33. image3x3
  34. ];
  35. ifdescribe(process.platform === 'darwin')('isMacTemplateImage state', () => {
  36. describe('with properties', () => {
  37. it('correctly recognizes a template image', () => {
  38. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  39. expect(image.isMacTemplateImage).to.be.false();
  40. const templateImage = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo_Template.png'));
  41. expect(templateImage.isMacTemplateImage).to.be.true();
  42. });
  43. it('sets a template image', function () {
  44. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  45. expect(image.isMacTemplateImage).to.be.false();
  46. image.isMacTemplateImage = true;
  47. expect(image.isMacTemplateImage).to.be.true();
  48. });
  49. });
  50. describe('with functions', () => {
  51. it('correctly recognizes a template image', () => {
  52. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  53. expect(image.isTemplateImage()).to.be.false();
  54. const templateImage = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo_Template.png'));
  55. expect(templateImage.isTemplateImage()).to.be.true();
  56. });
  57. it('sets a template image', function () {
  58. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  59. expect(image.isTemplateImage()).to.be.false();
  60. image.setTemplateImage(true);
  61. expect(image.isTemplateImage()).to.be.true();
  62. });
  63. });
  64. });
  65. describe('createEmpty()', () => {
  66. it('returns an empty image', () => {
  67. const empty = nativeImage.createEmpty();
  68. expect(empty.isEmpty()).to.be.true();
  69. expect(empty.getAspectRatio()).to.equal(1);
  70. expect(empty.toDataURL()).to.equal('data:image/png;base64,');
  71. expect(empty.toDataURL({ scaleFactor: 2.0 })).to.equal('data:image/png;base64,');
  72. expect(empty.getSize()).to.deep.equal({ width: 0, height: 0 });
  73. expect(empty.getBitmap()).to.be.empty();
  74. expect(empty.getBitmap({ scaleFactor: 2.0 })).to.be.empty();
  75. expect(empty.toBitmap()).to.be.empty();
  76. expect(empty.toBitmap({ scaleFactor: 2.0 })).to.be.empty();
  77. expect(empty.toJPEG(100)).to.be.empty();
  78. expect(empty.toPNG()).to.be.empty();
  79. expect(empty.toPNG({ scaleFactor: 2.0 })).to.be.empty();
  80. if (process.platform === 'darwin') {
  81. expect(empty.getNativeHandle()).to.be.empty();
  82. }
  83. });
  84. });
  85. describe('createFromBitmap(buffer, options)', () => {
  86. it('returns an empty image when the buffer is empty', () => {
  87. expect(nativeImage.createFromBitmap(Buffer.from([]), { width: 0, height: 0 }).isEmpty()).to.be.true();
  88. });
  89. it('returns an image created from the given buffer', () => {
  90. const imageA = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  91. const imageB = nativeImage.createFromBitmap(imageA.toBitmap(), imageA.getSize());
  92. expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 });
  93. const imageC = nativeImage.createFromBuffer(imageA.toBitmap(), { ...imageA.getSize(), scaleFactor: 2.0 });
  94. expect(imageC.getSize()).to.deep.equal({ width: 269, height: 95 });
  95. });
  96. it('throws on invalid arguments', () => {
  97. expect(() => nativeImage.createFromBitmap(null as any, {} as any)).to.throw('buffer must be a node Buffer');
  98. expect(() => nativeImage.createFromBitmap([12, 14, 124, 12] as any, {} as any)).to.throw('buffer must be a node Buffer');
  99. expect(() => nativeImage.createFromBitmap(Buffer.from([]), {} as any)).to.throw('width is required');
  100. expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1 } as any)).to.throw('height is required');
  101. expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1, height: 1 })).to.throw('invalid buffer size');
  102. });
  103. });
  104. describe('createFromBuffer(buffer, options)', () => {
  105. it('returns an empty image when the buffer is empty', () => {
  106. expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty()).to.be.true();
  107. });
  108. it('returns an empty image when the buffer is too small', () => {
  109. const image = nativeImage.createFromBuffer(Buffer.from([1, 2, 3, 4]), { width: 100, height: 100 });
  110. expect(image.isEmpty()).to.be.true();
  111. });
  112. it('returns an image created from the given buffer', () => {
  113. const imageA = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  114. const imageB = nativeImage.createFromBuffer(imageA.toPNG());
  115. expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 });
  116. expect(imageA.toBitmap().equals(imageB.toBitmap())).to.be.true();
  117. const imageC = nativeImage.createFromBuffer(imageA.toJPEG(100));
  118. expect(imageC.getSize()).to.deep.equal({ width: 538, height: 190 });
  119. const imageD = nativeImage.createFromBuffer(imageA.toBitmap(),
  120. { width: 538, height: 190 });
  121. expect(imageD.getSize()).to.deep.equal({ width: 538, height: 190 });
  122. const imageE = nativeImage.createFromBuffer(imageA.toBitmap(),
  123. { width: 100, height: 200 });
  124. expect(imageE.getSize()).to.deep.equal({ width: 100, height: 200 });
  125. const imageF = nativeImage.createFromBuffer(imageA.toBitmap());
  126. expect(imageF.isEmpty()).to.be.true();
  127. const imageG = nativeImage.createFromBuffer(imageA.toPNG(),
  128. { width: 100, height: 200 });
  129. expect(imageG.getSize()).to.deep.equal({ width: 538, height: 190 });
  130. const imageH = nativeImage.createFromBuffer(imageA.toJPEG(100),
  131. { width: 100, height: 200 });
  132. expect(imageH.getSize()).to.deep.equal({ width: 538, height: 190 });
  133. const imageI = nativeImage.createFromBuffer(imageA.toBitmap(),
  134. { width: 538, height: 190, scaleFactor: 2.0 });
  135. expect(imageI.getSize()).to.deep.equal({ width: 269, height: 95 });
  136. });
  137. it('throws on invalid arguments', () => {
  138. expect(() => nativeImage.createFromBuffer(null as any)).to.throw('buffer must be a node Buffer');
  139. expect(() => nativeImage.createFromBuffer([12, 14, 124, 12] as any)).to.throw('buffer must be a node Buffer');
  140. });
  141. });
  142. describe('createFromDataURL(dataURL)', () => {
  143. it('returns an empty image from the empty string', () => {
  144. expect(nativeImage.createFromDataURL('').isEmpty()).to.be.true();
  145. });
  146. it('returns an image created from the given string', () => {
  147. for (const imageData of dataUrlImages) {
  148. const imageFromPath = nativeImage.createFromPath(imageData.path);
  149. const imageFromDataUrl = nativeImage.createFromDataURL(imageData.dataUrl!);
  150. expect(imageFromDataUrl.isEmpty()).to.be.false();
  151. expect(imageFromDataUrl.getSize()).to.deep.equal(imageFromPath.getSize());
  152. expect(imageFromDataUrl.toBitmap()).to.satisfy(
  153. (bitmap: any) => imageFromPath.toBitmap().equals(bitmap));
  154. expect(imageFromDataUrl.toDataURL()).to.equal(imageFromPath.toDataURL());
  155. }
  156. });
  157. });
  158. describe('toDataURL()', () => {
  159. it('returns a PNG data URL', () => {
  160. for (const imageData of dataUrlImages) {
  161. const imageFromPath = nativeImage.createFromPath(imageData.path!);
  162. const scaleFactors = [1.0, 2.0];
  163. for (const scaleFactor of scaleFactors) {
  164. expect(imageFromPath.toDataURL({ scaleFactor })).to.equal(imageData.dataUrl);
  165. }
  166. }
  167. });
  168. it('returns a data URL at 1x scale factor by default', () => {
  169. const imageData = imageLogo;
  170. const image = nativeImage.createFromPath(imageData.path);
  171. const imageOne = nativeImage.createFromBuffer(image.toPNG(), {
  172. width: image.getSize().width,
  173. height: image.getSize().height,
  174. scaleFactor: 2.0
  175. });
  176. expect(imageOne.getSize()).to.deep.equal(
  177. { width: imageData.width / 2, height: imageData.height / 2 });
  178. const imageTwo = nativeImage.createFromDataURL(imageOne.toDataURL());
  179. expect(imageTwo.getSize()).to.deep.equal(
  180. { width: imageData.width, height: imageData.height });
  181. expect(imageOne.toBitmap().equals(imageTwo.toBitmap())).to.be.true();
  182. });
  183. it('supports a scale factor', () => {
  184. const imageData = imageLogo;
  185. const image = nativeImage.createFromPath(imageData.path);
  186. const expectedSize = { width: imageData.width, height: imageData.height };
  187. const imageFromDataUrlOne = nativeImage.createFromDataURL(
  188. image.toDataURL({ scaleFactor: 1.0 }));
  189. expect(imageFromDataUrlOne.getSize()).to.deep.equal(expectedSize);
  190. const imageFromDataUrlTwo = nativeImage.createFromDataURL(
  191. image.toDataURL({ scaleFactor: 2.0 }));
  192. expect(imageFromDataUrlTwo.getSize()).to.deep.equal(expectedSize);
  193. });
  194. });
  195. describe('toPNG()', () => {
  196. it('returns a buffer at 1x scale factor by default', () => {
  197. const imageData = imageLogo;
  198. const imageA = nativeImage.createFromPath(imageData.path);
  199. const imageB = nativeImage.createFromBuffer(imageA.toPNG(), {
  200. width: imageA.getSize().width,
  201. height: imageA.getSize().height,
  202. scaleFactor: 2.0
  203. });
  204. expect(imageB.getSize()).to.deep.equal(
  205. { width: imageData.width / 2, height: imageData.height / 2 });
  206. const imageC = nativeImage.createFromBuffer(imageB.toPNG());
  207. expect(imageC.getSize()).to.deep.equal(
  208. { width: imageData.width, height: imageData.height });
  209. expect(imageB.toBitmap().equals(imageC.toBitmap())).to.be.true();
  210. });
  211. it('supports a scale factor', () => {
  212. const imageData = imageLogo;
  213. const image = nativeImage.createFromPath(imageData.path);
  214. const imageFromBufferOne = nativeImage.createFromBuffer(
  215. image.toPNG({ scaleFactor: 1.0 }));
  216. expect(imageFromBufferOne.getSize()).to.deep.equal(
  217. { width: imageData.width, height: imageData.height });
  218. const imageFromBufferTwo = nativeImage.createFromBuffer(
  219. image.toPNG({ scaleFactor: 2.0 }), { scaleFactor: 2.0 });
  220. expect(imageFromBufferTwo.getSize()).to.deep.equal(
  221. { width: imageData.width / 2, height: imageData.height / 2 });
  222. });
  223. });
  224. describe('createFromPath(path)', () => {
  225. it('returns an empty image for invalid paths', () => {
  226. expect(nativeImage.createFromPath('').isEmpty()).to.be.true();
  227. expect(nativeImage.createFromPath('does-not-exist.png').isEmpty()).to.be.true();
  228. expect(nativeImage.createFromPath('does-not-exist.ico').isEmpty()).to.be.true();
  229. expect(nativeImage.createFromPath(__dirname).isEmpty()).to.be.true();
  230. expect(nativeImage.createFromPath(__filename).isEmpty()).to.be.true();
  231. });
  232. it('loads images from paths relative to the current working directory', () => {
  233. const imagePath = path.relative('.', path.join(fixturesPath, 'assets', 'logo.png'));
  234. const image = nativeImage.createFromPath(imagePath);
  235. expect(image.isEmpty()).to.be.false();
  236. expect(image.getSize()).to.deep.equal({ width: 538, height: 190 });
  237. });
  238. it('loads images from paths with `.` segments', () => {
  239. const imagePath = `${path.join(fixturesPath)}${path.sep}.${path.sep}${path.join('assets', 'logo.png')}`;
  240. const image = nativeImage.createFromPath(imagePath);
  241. expect(image.isEmpty()).to.be.false();
  242. expect(image.getSize()).to.deep.equal({ width: 538, height: 190 });
  243. });
  244. it('loads images from paths with `..` segments', () => {
  245. const imagePath = `${path.join(fixturesPath, 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`;
  246. const image = nativeImage.createFromPath(imagePath);
  247. expect(image.isEmpty()).to.be.false();
  248. expect(image.getSize()).to.deep.equal({ width: 538, height: 190 });
  249. });
  250. ifit(process.platform === 'darwin')('Gets an NSImage pointer on macOS', function () {
  251. const imagePath = `${path.join(fixturesPath, 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`;
  252. const image = nativeImage.createFromPath(imagePath);
  253. const nsimage = image.getNativeHandle();
  254. expect(nsimage).to.have.lengthOf(8);
  255. // If all bytes are null, that's Bad
  256. const allBytesAreNotNull = nsimage.reduce((acc, x) => acc || (x !== 0), false);
  257. expect(allBytesAreNotNull);
  258. });
  259. ifit(process.platform === 'win32')('loads images from .ico files on Windows', function () {
  260. const imagePath = path.join(fixturesPath, 'assets', 'icon.ico');
  261. const image = nativeImage.createFromPath(imagePath);
  262. expect(image.isEmpty()).to.be.false();
  263. expect(image.getSize()).to.deep.equal({ width: 256, height: 256 });
  264. });
  265. });
  266. describe('createFromNamedImage(name)', () => {
  267. it('returns empty for invalid options', () => {
  268. const image = nativeImage.createFromNamedImage('totally_not_real');
  269. expect(image.isEmpty()).to.be.true();
  270. });
  271. ifit(process.platform !== 'darwin')('returns empty on non-darwin platforms', function () {
  272. const image = nativeImage.createFromNamedImage('NSActionTemplate');
  273. expect(image.isEmpty()).to.be.true();
  274. });
  275. ifit(process.platform === 'darwin')('returns a valid image on darwin', function () {
  276. const image = nativeImage.createFromNamedImage('NSActionTemplate');
  277. expect(image.isEmpty()).to.be.false();
  278. });
  279. ifit(process.platform === 'darwin')('returns allows an HSL shift for a valid image on darwin', function () {
  280. const image = nativeImage.createFromNamedImage('NSActionTemplate', [0.5, 0.2, 0.8]);
  281. expect(image.isEmpty()).to.be.false();
  282. });
  283. });
  284. describe('resize(options)', () => {
  285. it('returns a resized image', () => {
  286. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  287. for (const [resizeTo, expectedSize] of new Map([
  288. [{}, { width: 538, height: 190 }],
  289. [{ width: 269 }, { width: 269, height: 95 }],
  290. [{ width: 600 }, { width: 600, height: 212 }],
  291. [{ height: 95 }, { width: 269, height: 95 }],
  292. [{ height: 200 }, { width: 566, height: 200 }],
  293. [{ width: 80, height: 65 }, { width: 80, height: 65 }],
  294. [{ width: 600, height: 200 }, { width: 600, height: 200 }],
  295. [{ width: 0, height: 0 }, { width: 0, height: 0 }],
  296. [{ width: -1, height: -1 }, { width: 0, height: 0 }]
  297. ])) {
  298. const actualSize = image.resize(resizeTo).getSize();
  299. expect(actualSize).to.deep.equal(expectedSize);
  300. }
  301. });
  302. it('returns an empty image when called on an empty image', () => {
  303. expect(nativeImage.createEmpty().resize({ width: 1, height: 1 }).isEmpty()).to.be.true();
  304. expect(nativeImage.createEmpty().resize({ width: 0, height: 0 }).isEmpty()).to.be.true();
  305. });
  306. it('supports a quality option', () => {
  307. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  308. const good = image.resize({ width: 100, height: 100, quality: 'good' });
  309. const better = image.resize({ width: 100, height: 100, quality: 'better' });
  310. const best = image.resize({ width: 100, height: 100, quality: 'best' });
  311. expect(good.toPNG()).to.have.lengthOf.at.most(better.toPNG().length);
  312. expect(better.toPNG()).to.have.lengthOf.below(best.toPNG().length);
  313. });
  314. });
  315. describe('crop(bounds)', () => {
  316. it('returns an empty image when called on an empty image', () => {
  317. expect(nativeImage.createEmpty().crop({ width: 1, height: 2, x: 0, y: 0 }).isEmpty()).to.be.true();
  318. expect(nativeImage.createEmpty().crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true();
  319. });
  320. it('returns an empty image when the bounds are invalid', () => {
  321. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  322. expect(image.crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true();
  323. expect(image.crop({ width: -1, height: 10, x: 0, y: 0 }).isEmpty()).to.be.true();
  324. expect(image.crop({ width: 10, height: -35, x: 0, y: 0 }).isEmpty()).to.be.true();
  325. expect(image.crop({ width: 100, height: 100, x: 1000, y: 1000 }).isEmpty()).to.be.true();
  326. });
  327. it('returns a cropped image', () => {
  328. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  329. const cropA = image.crop({ width: 25, height: 64, x: 0, y: 0 });
  330. const cropB = image.crop({ width: 25, height: 64, x: 30, y: 40 });
  331. expect(cropA.getSize()).to.deep.equal({ width: 25, height: 64 });
  332. expect(cropB.getSize()).to.deep.equal({ width: 25, height: 64 });
  333. expect(cropA.toPNG().equals(cropB.toPNG())).to.be.false();
  334. });
  335. it('toBitmap() returns a buffer of the right size', () => {
  336. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  337. const crop = image.crop({ width: 25, height: 64, x: 0, y: 0 });
  338. expect(crop.toBitmap().length).to.equal(25 * 64 * 4);
  339. });
  340. });
  341. describe('getAspectRatio()', () => {
  342. it('returns an aspect ratio of an empty image', () => {
  343. expect(nativeImage.createEmpty().getAspectRatio()).to.equal(1.0);
  344. });
  345. it('returns an aspect ratio of an image', () => {
  346. const imageData = imageLogo;
  347. // imageData.width / imageData.height = 2.831578947368421
  348. const expectedAspectRatio = 2.8315789699554443;
  349. const image = nativeImage.createFromPath(imageData.path);
  350. expect(image.getAspectRatio()).to.equal(expectedAspectRatio);
  351. });
  352. });
  353. ifdescribe(process.platform !== 'linux')('createThumbnailFromPath(path, size)', () => {
  354. it('throws when invalid size is passed', async () => {
  355. const badSize = { width: -1, height: -1 };
  356. await expect(
  357. nativeImage.createThumbnailFromPath('path', badSize)
  358. ).to.eventually.be.rejectedWith('size must not be empty');
  359. });
  360. it('throws when a bad path is passed', async () => {
  361. const badPath = process.platform === 'win32' ? '\\hey\\hi\\hello' : '/hey/hi/hello';
  362. const goodSize = { width: 100, height: 100 };
  363. await expect(
  364. nativeImage.createThumbnailFromPath(badPath, goodSize)
  365. ).to.eventually.be.rejected();
  366. });
  367. it('returns native image given valid params', async () => {
  368. const goodPath = path.join(fixturesPath, 'assets', 'logo.png');
  369. const goodSize = { width: 100, height: 100 };
  370. const result = await nativeImage.createThumbnailFromPath(goodPath, goodSize);
  371. expect(result.isEmpty()).to.equal(false);
  372. });
  373. it('returns the correct size if larger than the initial image', async () => {
  374. // capybara.png is a 128x128 image.
  375. const imgPath = path.join(fixturesPath, 'assets', 'capybara.png');
  376. const size = { width: 256, height: 256 };
  377. const result = await nativeImage.createThumbnailFromPath(imgPath, size);
  378. expect(result.getSize()).to.deep.equal(size);
  379. });
  380. it('returns the correct size if is the same as the initial image', async () => {
  381. // capybara.png is a 128x128 image.
  382. const imgPath = path.join(fixturesPath, 'assets', 'capybara.png');
  383. const size = { width: 128, height: 128 };
  384. const result = await nativeImage.createThumbnailFromPath(imgPath, size);
  385. expect(result.getSize()).to.deep.equal(size);
  386. });
  387. it('returns the correct size if smaller than the initial image', async () => {
  388. // capybara.png is a 128x128 image.
  389. const imgPath = path.join(fixturesPath, 'assets', 'capybara.png');
  390. const maxSize = { width: 64, height: 64 };
  391. const result = await nativeImage.createThumbnailFromPath(imgPath, maxSize);
  392. expect(result.getSize()).to.deep.equal(maxSize);
  393. });
  394. });
  395. describe('addRepresentation()', () => {
  396. it('does not add representation when the buffer is too small', () => {
  397. const image = nativeImage.createEmpty();
  398. image.addRepresentation({
  399. buffer: Buffer.from([1, 2, 3, 4]),
  400. width: 100,
  401. height: 100
  402. });
  403. expect(image.isEmpty()).to.be.true();
  404. });
  405. it('supports adding a buffer representation for a scale factor', () => {
  406. const image = nativeImage.createEmpty();
  407. const imageDataOne = image1x1;
  408. image.addRepresentation({
  409. scaleFactor: 1.0,
  410. buffer: nativeImage.createFromPath(imageDataOne.path).toPNG()
  411. });
  412. expect(image.getScaleFactors()).to.deep.equal([1]);
  413. const imageDataTwo = image2x2;
  414. image.addRepresentation({
  415. scaleFactor: 2.0,
  416. buffer: nativeImage.createFromPath(imageDataTwo.path).toPNG()
  417. });
  418. expect(image.getScaleFactors()).to.deep.equal([1, 2]);
  419. const imageDataThree = image3x3;
  420. image.addRepresentation({
  421. scaleFactor: 3.0,
  422. buffer: nativeImage.createFromPath(imageDataThree.path).toPNG()
  423. });
  424. expect(image.getScaleFactors()).to.deep.equal([1, 2, 3]);
  425. image.addRepresentation({
  426. scaleFactor: 4.0,
  427. buffer: 'invalid' as any
  428. });
  429. // this one failed, so it shouldn't show up in the scale factors
  430. expect(image.getScaleFactors()).to.deep.equal([1, 2, 3]);
  431. expect(image.isEmpty()).to.be.false();
  432. expect(image.getSize()).to.deep.equal({ width: 1, height: 1 });
  433. expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl);
  434. expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl);
  435. expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl);
  436. expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl);
  437. });
  438. it('supports adding a data URL representation for a scale factor', () => {
  439. const image = nativeImage.createEmpty();
  440. const imageDataOne = image1x1;
  441. image.addRepresentation({
  442. scaleFactor: 1.0,
  443. dataURL: imageDataOne.dataUrl
  444. });
  445. const imageDataTwo = image2x2;
  446. image.addRepresentation({
  447. scaleFactor: 2.0,
  448. dataURL: imageDataTwo.dataUrl
  449. });
  450. const imageDataThree = image3x3;
  451. image.addRepresentation({
  452. scaleFactor: 3.0,
  453. dataURL: imageDataThree.dataUrl
  454. });
  455. image.addRepresentation({
  456. scaleFactor: 4.0,
  457. dataURL: 'invalid'
  458. });
  459. expect(image.isEmpty()).to.be.false();
  460. expect(image.getSize()).to.deep.equal({ width: 1, height: 1 });
  461. expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl);
  462. expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl);
  463. expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl);
  464. expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl);
  465. });
  466. it('supports adding a representation to an existing image', () => {
  467. const imageDataOne = image1x1;
  468. const image = nativeImage.createFromPath(imageDataOne.path);
  469. const imageDataTwo = image2x2;
  470. image.addRepresentation({
  471. scaleFactor: 2.0,
  472. dataURL: imageDataTwo.dataUrl
  473. });
  474. const imageDataThree = image3x3;
  475. image.addRepresentation({
  476. scaleFactor: 2.0,
  477. dataURL: imageDataThree.dataUrl
  478. });
  479. expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl);
  480. expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl);
  481. });
  482. });
  483. });