images.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import fs from 'fs';
  2. import { PNG } from 'pngjs';
  3. import { Page } from 'puppeteer-core';
  4. import pixelmatch from 'pixelmatch';
  5. import { constants } from './constants';
  6. export const takeScreenShot = async (page: Page, fileName: string) => {
  7. const outputFolderExists = fs.existsSync(constants.screenShotsOutputDir);
  8. if (!outputFolderExists) {
  9. fs.mkdirSync(constants.screenShotsOutputDir);
  10. }
  11. const path = `${constants.screenShotsOutputDir}/${fileName}.png`;
  12. await page.screenshot({ path, type: 'png', fullPage: false });
  13. };
  14. export const compareScreenShots = async (fileName: string) =>
  15. new Promise(resolve => {
  16. let filesRead = 0;
  17. const doneReading = () => {
  18. if (++filesRead < 2) {
  19. return;
  20. }
  21. if (screenShotFromTest.width !== screenShotFromTruth.width) {
  22. throw new Error(
  23. `The screenshot:[${fileName}] taken during the test has a ` +
  24. `width:[${screenShotFromTest.width}] that differs from the ` +
  25. `expected: [${screenShotFromTruth.width}].`
  26. );
  27. }
  28. if (screenShotFromTest.height !== screenShotFromTruth.height) {
  29. throw new Error(
  30. `The screenshot:[${fileName}] taken during the test has a ` +
  31. `height:[${screenShotFromTest.height}] that differs from the ` +
  32. `expected: [${screenShotFromTruth.height}].`
  33. );
  34. }
  35. const diff = new PNG({ width: screenShotFromTest.width, height: screenShotFromTruth.height });
  36. const numDiffPixels = pixelmatch(
  37. screenShotFromTest.data,
  38. screenShotFromTruth.data,
  39. diff.data,
  40. screenShotFromTest.width,
  41. screenShotFromTest.height,
  42. { threshold: 0.1 }
  43. );
  44. if (numDiffPixels !== 0) {
  45. const localMessage =
  46. `\nCompare the output from expected:[${constants.screenShotsTruthDir}] ` +
  47. `with outcome:[${constants.screenShotsOutputDir}]`;
  48. const circleCIMessage = '\nCheck the Artifacts tab in the CircleCi build output for the actual screenshots.';
  49. const checkMessage = process.env.CIRCLE_SHA1 ? circleCIMessage : localMessage;
  50. let msg =
  51. `\nThe screenshot:[${constants.screenShotsOutputDir}/${fileName}.png] ` +
  52. `taken during the test differs by:[${numDiffPixels}] pixels from the expected.`;
  53. msg += '\n';
  54. msg += checkMessage;
  55. msg += '\n';
  56. msg += '\n If the difference between expected and outcome is NOT acceptable then do the following:';
  57. msg += '\n - Check the code for changes that causes this difference, fix that and retry.';
  58. msg += '\n';
  59. msg += '\n If the difference between expected and outcome is acceptable then do the following:';
  60. msg += '\n - Replace the expected image with the outcome and retry.';
  61. msg += '\n';
  62. throw new Error(msg);
  63. }
  64. resolve();
  65. };
  66. const screenShotFromTest = fs
  67. .createReadStream(`${constants.screenShotsOutputDir}/${fileName}.png`)
  68. .pipe(new PNG())
  69. .on('parsed', doneReading);
  70. const screenShotFromTruth = fs
  71. .createReadStream(`${constants.screenShotsTruthDir}/${fileName}.png`)
  72. .pipe(new PNG())
  73. .on('parsed', doneReading);
  74. });