api-native-image-spec.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. import { nativeImage } from 'electron/common';
  2. import { expect } from 'chai';
  3. import * as path from 'node:path';
  4. import { ifdescribe, ifit, itremote, useRemoteContext } from './lib/spec-helpers';
  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,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAtJREFUGJVjYAACAAAFAAHWnxbNAAAAAElFTkSuQmCC',
  14. path: path.join(fixturesPath, 'assets', '1x1.png'),
  15. height: 1,
  16. width: 1
  17. };
  18. const image2x2 = {
  19. dataUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAAXNSR0IArs4c6QAAABZJREFUGJVj/P//PwMDAxMDAwMDAwMAJAYDAQTM5XcAAAAASUVORK5CYII=',
  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/AAABB2lDQ1BTa2lhAAAokX2QP0vDQByGn1NBBEEHBweHG1wttQXp4FSF4BojpN0uMUYh/7ikdHURZ0GcRfwM9YOILi79CA7irJeA1yW+08PLy93DD8QcYKULaVZp1xlKfzSWq3MEgjoqLAvaI+D7vdm+7v2za8vaeVSGwCfgaX80BnEMbMUN+4aDhhPD06qofr+9Nqw99wjEPbAZL3CwwGGhzf4ZOEyTSWi9WY+ys1NgAOxQ4uIwROKgSJlQIZlyRcUlkh4dekg8NIqMkgsijLJsnswfYfAFy3e2Cx7g5Ra2P2y3+wQbNzB7s529YaG0+rvOUr/f4ixrZ4ecnJiECMkJGSGd2rXLPgc/b9ZFPoj6CWEAAAAMSURBVBiVY2AgCgAAACcAAZlYQycAAAAASUVORK5CYII=',
  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.createFromBuffer(
  149. nativeImage.createFromPath(imageData.path).toPNG());
  150. const imageFromDataUrl = nativeImage.createFromDataURL(imageData.dataUrl!);
  151. expect(imageFromDataUrl.isEmpty()).to.be.false();
  152. expect(imageFromDataUrl.getSize()).to.deep.equal(imageFromPath.getSize());
  153. expect(imageFromDataUrl.toBitmap()).to.satisfy(
  154. (bitmap: any) => imageFromPath.toBitmap().equals(bitmap));
  155. expect(imageFromDataUrl.toDataURL()).to.equal(imageFromPath.toDataURL());
  156. }
  157. });
  158. });
  159. describe('toDataURL()', () => {
  160. it('returns a PNG data URL', () => {
  161. for (const imageData of dataUrlImages) {
  162. const imageFromPath = nativeImage.createFromBuffer(
  163. nativeImage.createFromPath(imageData.path!).toPNG());
  164. const scaleFactors = [1.0, 2.0];
  165. for (const scaleFactor of scaleFactors) {
  166. expect(imageFromPath.toDataURL({ scaleFactor })).to.equal(imageData.dataUrl);
  167. }
  168. }
  169. });
  170. it('returns a data URL at 1x scale factor by default', () => {
  171. const imageData = imageLogo;
  172. const image = nativeImage.createFromPath(imageData.path);
  173. const imageOne = nativeImage.createFromBuffer(image.toPNG(), {
  174. width: image.getSize().width,
  175. height: image.getSize().height,
  176. scaleFactor: 2.0
  177. });
  178. expect(imageOne.getSize()).to.deep.equal(
  179. { width: imageData.width / 2, height: imageData.height / 2 });
  180. const imageTwo = nativeImage.createFromDataURL(imageOne.toDataURL());
  181. expect(imageTwo.getSize()).to.deep.equal(
  182. { width: imageData.width, height: imageData.height });
  183. expect(imageOne.toBitmap().equals(imageTwo.toBitmap())).to.be.true();
  184. });
  185. it('supports a scale factor', () => {
  186. const imageData = imageLogo;
  187. const image = nativeImage.createFromPath(imageData.path);
  188. const expectedSize = { width: imageData.width, height: imageData.height };
  189. const imageFromDataUrlOne = nativeImage.createFromDataURL(
  190. image.toDataURL({ scaleFactor: 1.0 }));
  191. expect(imageFromDataUrlOne.getSize()).to.deep.equal(expectedSize);
  192. const imageFromDataUrlTwo = nativeImage.createFromDataURL(
  193. image.toDataURL({ scaleFactor: 2.0 }));
  194. expect(imageFromDataUrlTwo.getSize()).to.deep.equal(expectedSize);
  195. });
  196. });
  197. describe('toPNG()', () => {
  198. it('returns a buffer at 1x scale factor by default', () => {
  199. const imageData = imageLogo;
  200. const imageA = nativeImage.createFromPath(imageData.path);
  201. const imageB = nativeImage.createFromBuffer(imageA.toPNG(), {
  202. width: imageA.getSize().width,
  203. height: imageA.getSize().height,
  204. scaleFactor: 2.0
  205. });
  206. expect(imageB.getSize()).to.deep.equal(
  207. { width: imageData.width / 2, height: imageData.height / 2 });
  208. const imageC = nativeImage.createFromBuffer(imageB.toPNG());
  209. expect(imageC.getSize()).to.deep.equal(
  210. { width: imageData.width, height: imageData.height });
  211. expect(imageB.toBitmap().equals(imageC.toBitmap())).to.be.true();
  212. });
  213. it('supports a scale factor', () => {
  214. const imageData = imageLogo;
  215. const image = nativeImage.createFromPath(imageData.path);
  216. const imageFromBufferOne = nativeImage.createFromBuffer(
  217. image.toPNG({ scaleFactor: 1.0 }));
  218. expect(imageFromBufferOne.getSize()).to.deep.equal(
  219. { width: imageData.width, height: imageData.height });
  220. const imageFromBufferTwo = nativeImage.createFromBuffer(
  221. image.toPNG({ scaleFactor: 2.0 }), { scaleFactor: 2.0 });
  222. expect(imageFromBufferTwo.getSize()).to.deep.equal(
  223. { width: imageData.width / 2, height: imageData.height / 2 });
  224. });
  225. });
  226. describe('createFromPath(path)', () => {
  227. it('returns an empty image for invalid paths', () => {
  228. expect(nativeImage.createFromPath('').isEmpty()).to.be.true();
  229. expect(nativeImage.createFromPath('does-not-exist.png').isEmpty()).to.be.true();
  230. expect(nativeImage.createFromPath('does-not-exist.ico').isEmpty()).to.be.true();
  231. expect(nativeImage.createFromPath(__dirname).isEmpty()).to.be.true();
  232. expect(nativeImage.createFromPath(__filename).isEmpty()).to.be.true();
  233. });
  234. it('loads images from paths relative to the current working directory', () => {
  235. const imagePath = path.relative('.', path.join(fixturesPath, 'assets', 'logo.png'));
  236. const image = nativeImage.createFromPath(imagePath);
  237. expect(image.isEmpty()).to.be.false();
  238. expect(image.getSize()).to.deep.equal({ width: 538, height: 190 });
  239. });
  240. it('loads images from paths with `.` segments', () => {
  241. const imagePath = `${path.join(fixturesPath)}${path.sep}.${path.sep}${path.join('assets', 'logo.png')}`;
  242. const image = nativeImage.createFromPath(imagePath);
  243. expect(image.isEmpty()).to.be.false();
  244. expect(image.getSize()).to.deep.equal({ width: 538, height: 190 });
  245. });
  246. it('loads images from paths with `..` segments', () => {
  247. const imagePath = `${path.join(fixturesPath, 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`;
  248. const image = nativeImage.createFromPath(imagePath);
  249. expect(image.isEmpty()).to.be.false();
  250. expect(image.getSize()).to.deep.equal({ width: 538, height: 190 });
  251. });
  252. ifit(process.platform === 'darwin')('Gets an NSImage pointer on macOS', function () {
  253. const imagePath = `${path.join(fixturesPath, 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`;
  254. const image = nativeImage.createFromPath(imagePath);
  255. const nsimage = image.getNativeHandle();
  256. expect(nsimage).to.have.lengthOf(8);
  257. // If all bytes are null, that's Bad
  258. const allBytesAreNotNull = nsimage.reduce((acc, x) => acc || (x !== 0), false);
  259. expect(allBytesAreNotNull);
  260. });
  261. ifit(process.platform === 'win32')('loads images from .ico files on Windows', function () {
  262. const imagePath = path.join(fixturesPath, 'assets', 'icon.ico');
  263. const image = nativeImage.createFromPath(imagePath);
  264. expect(image.isEmpty()).to.be.false();
  265. expect(image.getSize()).to.deep.equal({ width: 256, height: 256 });
  266. });
  267. });
  268. describe('createFromNamedImage(name)', () => {
  269. it('returns empty for invalid options', () => {
  270. const image = nativeImage.createFromNamedImage('totally_not_real');
  271. expect(image.isEmpty()).to.be.true();
  272. });
  273. ifit(process.platform !== 'darwin')('returns empty on non-darwin platforms', function () {
  274. const image = nativeImage.createFromNamedImage('NSActionTemplate');
  275. expect(image.isEmpty()).to.be.true();
  276. });
  277. ifit(process.platform === 'darwin')('returns a valid image on darwin', function () {
  278. const image = nativeImage.createFromNamedImage('NSActionTemplate');
  279. expect(image.isEmpty()).to.be.false();
  280. });
  281. ifit(process.platform === 'darwin')('returns allows an HSL shift for a valid image on darwin', function () {
  282. const image = nativeImage.createFromNamedImage('NSActionTemplate', [0.5, 0.2, 0.8]);
  283. expect(image.isEmpty()).to.be.false();
  284. });
  285. });
  286. describe('resize(options)', () => {
  287. it('returns a resized image', () => {
  288. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  289. for (const [resizeTo, expectedSize] of new Map([
  290. [{}, { width: 538, height: 190 }],
  291. [{ width: 269 }, { width: 269, height: 95 }],
  292. [{ width: 600 }, { width: 600, height: 212 }],
  293. [{ height: 95 }, { width: 269, height: 95 }],
  294. [{ height: 200 }, { width: 566, height: 200 }],
  295. [{ width: 80, height: 65 }, { width: 80, height: 65 }],
  296. [{ width: 600, height: 200 }, { width: 600, height: 200 }],
  297. [{ width: 0, height: 0 }, { width: 0, height: 0 }],
  298. [{ width: -1, height: -1 }, { width: 0, height: 0 }]
  299. ])) {
  300. const actualSize = image.resize(resizeTo).getSize();
  301. expect(actualSize).to.deep.equal(expectedSize);
  302. }
  303. });
  304. it('returns an empty image when called on an empty image', () => {
  305. expect(nativeImage.createEmpty().resize({ width: 1, height: 1 }).isEmpty()).to.be.true();
  306. expect(nativeImage.createEmpty().resize({ width: 0, height: 0 }).isEmpty()).to.be.true();
  307. });
  308. it('supports a quality option', () => {
  309. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  310. const good = image.resize({ width: 100, height: 100, quality: 'good' });
  311. const better = image.resize({ width: 100, height: 100, quality: 'better' });
  312. const best = image.resize({ width: 100, height: 100, quality: 'best' });
  313. expect(good.toPNG()).to.have.lengthOf.at.most(better.toPNG().length);
  314. expect(better.toPNG()).to.have.lengthOf.below(best.toPNG().length);
  315. });
  316. });
  317. describe('crop(bounds)', () => {
  318. it('returns an empty image when called on an empty image', () => {
  319. expect(nativeImage.createEmpty().crop({ width: 1, height: 2, x: 0, y: 0 }).isEmpty()).to.be.true();
  320. expect(nativeImage.createEmpty().crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true();
  321. });
  322. it('returns an empty image when the bounds are invalid', () => {
  323. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  324. expect(image.crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true();
  325. expect(image.crop({ width: -1, height: 10, x: 0, y: 0 }).isEmpty()).to.be.true();
  326. expect(image.crop({ width: 10, height: -35, x: 0, y: 0 }).isEmpty()).to.be.true();
  327. expect(image.crop({ width: 100, height: 100, x: 1000, y: 1000 }).isEmpty()).to.be.true();
  328. });
  329. it('returns a cropped image', () => {
  330. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  331. const cropA = image.crop({ width: 25, height: 64, x: 0, y: 0 });
  332. const cropB = image.crop({ width: 25, height: 64, x: 30, y: 40 });
  333. expect(cropA.getSize()).to.deep.equal({ width: 25, height: 64 });
  334. expect(cropB.getSize()).to.deep.equal({ width: 25, height: 64 });
  335. expect(cropA.toPNG().equals(cropB.toPNG())).to.be.false();
  336. });
  337. it('toBitmap() returns a buffer of the right size', () => {
  338. const image = nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'logo.png'));
  339. const crop = image.crop({ width: 25, height: 64, x: 0, y: 0 });
  340. expect(crop.toBitmap().length).to.equal(25 * 64 * 4);
  341. });
  342. });
  343. describe('getAspectRatio()', () => {
  344. it('returns an aspect ratio of an empty image', () => {
  345. expect(nativeImage.createEmpty().getAspectRatio()).to.equal(1.0);
  346. });
  347. it('returns an aspect ratio of an image', () => {
  348. const imageData = imageLogo;
  349. // imageData.width / imageData.height = 2.831578947368421
  350. const expectedAspectRatio = 2.8315789699554443;
  351. const image = nativeImage.createFromPath(imageData.path);
  352. expect(image.getAspectRatio()).to.equal(expectedAspectRatio);
  353. });
  354. });
  355. ifdescribe(process.platform !== 'linux')('createThumbnailFromPath(path, size)', () => {
  356. useRemoteContext({ webPreferences: { contextIsolation: false, nodeIntegration: true } });
  357. it('throws when invalid size is passed', async () => {
  358. const badSize = { width: -1, height: -1 };
  359. await expect(
  360. nativeImage.createThumbnailFromPath('path', badSize)
  361. ).to.eventually.be.rejectedWith('size must not be empty');
  362. });
  363. it('throws when a bad path is passed', async () => {
  364. const badPath = process.platform === 'win32' ? '\\hey\\hi\\hello' : '/hey/hi/hello';
  365. const goodSize = { width: 100, height: 100 };
  366. await expect(
  367. nativeImage.createThumbnailFromPath(badPath, goodSize)
  368. ).to.eventually.be.rejected();
  369. });
  370. it('returns native image given valid params', async () => {
  371. const goodPath = path.join(fixturesPath, 'assets', 'logo.png');
  372. const goodSize = { width: 100, height: 100 };
  373. const result = await nativeImage.createThumbnailFromPath(goodPath, goodSize);
  374. expect(result.isEmpty()).to.equal(false);
  375. });
  376. it('returns the correct size if larger than the initial image', async () => {
  377. // capybara.png is a 128x128 image.
  378. const imgPath = path.join(fixturesPath, 'assets', 'capybara.png');
  379. const size = { width: 256, height: 256 };
  380. const result = await nativeImage.createThumbnailFromPath(imgPath, size);
  381. expect(result.getSize()).to.deep.equal(size);
  382. });
  383. it('returns the correct size if is the same as the initial image', async () => {
  384. // capybara.png is a 128x128 image.
  385. const imgPath = path.join(fixturesPath, 'assets', 'capybara.png');
  386. const size = { width: 128, height: 128 };
  387. const result = await nativeImage.createThumbnailFromPath(imgPath, size);
  388. expect(result.getSize()).to.deep.equal(size);
  389. });
  390. it('returns the correct size if smaller than the initial image', async () => {
  391. // capybara.png is a 128x128 image.
  392. const imgPath = path.join(fixturesPath, 'assets', 'capybara.png');
  393. const maxSize = { width: 64, height: 64 };
  394. const result = await nativeImage.createThumbnailFromPath(imgPath, maxSize);
  395. expect(result.getSize()).to.deep.equal(maxSize);
  396. });
  397. itremote('works in the renderer', async (path: string) => {
  398. const { nativeImage } = require('electron');
  399. const goodSize = { width: 100, height: 100 };
  400. const result = await nativeImage.createThumbnailFromPath(path, goodSize);
  401. expect(result.isEmpty()).to.equal(false);
  402. }, [path.join(fixturesPath, 'assets', 'logo.png')]);
  403. });
  404. describe('addRepresentation()', () => {
  405. it('does not add representation when the buffer is too small', () => {
  406. const image = nativeImage.createEmpty();
  407. image.addRepresentation({
  408. buffer: Buffer.from([1, 2, 3, 4]),
  409. width: 100,
  410. height: 100
  411. });
  412. expect(image.isEmpty()).to.be.true();
  413. });
  414. it('supports adding a buffer representation for a scale factor', () => {
  415. const image = nativeImage.createEmpty();
  416. const imageDataOne = image1x1;
  417. image.addRepresentation({
  418. scaleFactor: 1.0,
  419. buffer: nativeImage.createFromPath(imageDataOne.path).toPNG()
  420. });
  421. expect(image.getScaleFactors()).to.deep.equal([1]);
  422. const imageDataTwo = image2x2;
  423. image.addRepresentation({
  424. scaleFactor: 2.0,
  425. buffer: nativeImage.createFromPath(imageDataTwo.path).toPNG()
  426. });
  427. expect(image.getScaleFactors()).to.deep.equal([1, 2]);
  428. const imageDataThree = image3x3;
  429. image.addRepresentation({
  430. scaleFactor: 3.0,
  431. buffer: nativeImage.createFromPath(imageDataThree.path).toPNG()
  432. });
  433. expect(image.getScaleFactors()).to.deep.equal([1, 2, 3]);
  434. image.addRepresentation({
  435. scaleFactor: 4.0,
  436. buffer: 'invalid' as any
  437. });
  438. // this one failed, so it shouldn't show up in the scale factors
  439. expect(image.getScaleFactors()).to.deep.equal([1, 2, 3]);
  440. expect(image.isEmpty()).to.be.false();
  441. expect(image.getSize()).to.deep.equal({ width: 1, height: 1 });
  442. expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl);
  443. expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl);
  444. expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl);
  445. expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl);
  446. });
  447. it('supports adding a data URL representation for a scale factor', () => {
  448. const image = nativeImage.createEmpty();
  449. const imageDataOne = image1x1;
  450. image.addRepresentation({
  451. scaleFactor: 1.0,
  452. dataURL: imageDataOne.dataUrl
  453. });
  454. const imageDataTwo = image2x2;
  455. image.addRepresentation({
  456. scaleFactor: 2.0,
  457. dataURL: imageDataTwo.dataUrl
  458. });
  459. const imageDataThree = image3x3;
  460. image.addRepresentation({
  461. scaleFactor: 3.0,
  462. dataURL: imageDataThree.dataUrl
  463. });
  464. image.addRepresentation({
  465. scaleFactor: 4.0,
  466. dataURL: 'invalid'
  467. });
  468. expect(image.isEmpty()).to.be.false();
  469. expect(image.getSize()).to.deep.equal({ width: 1, height: 1 });
  470. expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl);
  471. expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl);
  472. expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl);
  473. expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl);
  474. });
  475. it('supports adding a representation to an existing image', () => {
  476. const imageDataOne = image1x1;
  477. const image = nativeImage.createFromPath(imageDataOne.path);
  478. const imageDataTwo = image2x2;
  479. image.addRepresentation({
  480. scaleFactor: 2.0,
  481. dataURL: imageDataTwo.dataUrl
  482. });
  483. const imageDataThree = image3x3;
  484. image.addRepresentation({
  485. scaleFactor: 2.0,
  486. dataURL: imageDataThree.dataUrl
  487. });
  488. expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl);
  489. expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl);
  490. });
  491. });
  492. });