Donaldcwl / browser-image-compression

Image compression in web browser
MIT License
1.3k stars 160 forks source link

I can't get the imageCompression() function to work while running jest tests in react #129

Closed jimmythecode closed 2 years ago

jimmythecode commented 3 years ago

When I run a function (which I've got working correctly in codesandbox), in my React app with some Jest tests, the code seems to stall at:

    console.log(file.type, file.size, options);
    const output = await imageCompression(file, options);

The console prints:

  console.log
    image/jpeg 1826515 {
      maxSizeMB: 1,
      maxWidthOrHeight: 1024,
      useWebWorker: false,
      onProgress: [Function: onProgress]
    }

So I think everything is as it should be. The tests keep timing out and it's as if everything gets stuck when I run the imageCompression function. Does anyone know why, or how to fix this?

Donaldcwl commented 2 years ago

@jimmythecode Could you provide more information about your React app and the Jest tests. It will be easier to help if you can share code snippets that reproduce the issue.

jimmythecode commented 2 years ago

Apologies for the omission. Also, worth noting that the compressImageFile function works absolutely fine in production: imageHandling.test.ts:

describe.only("compressImageFile function", () => {
    test("use imageCompression() to compress file", async () => {
        // jest.setTimeout(30000);
        expect.assertions(4)
        const returnFile = await fetchImage(workingUrlLink, "http")
        if (typeof returnFile !== "string") {
            console.log({
                type: returnFile.type,
                size: returnFile.size
            }) // { type: 'image/jpeg', size: 87664 }
            const compressedFile = await compressImageFile(returnFile)
            expect(returnFile.type).toBe('image/jpeg');
            expect(returnFile.size).toBe(87664);
            expect(compressedFile.type).toBe('image/jpeg');
            expect(compressedFile.size).toBeLessThan(87664);

        }
    });
});

/imageHandling.ts:

export async function compressImageFile(file: File) {
    console.log("input", file); //  input File {}
    console.log(
        "ExifOrientation",
        await imageCompression.getExifOrientation(file)
    ); //  ExifOrientation -1
    const maxSizeMB = 1
    const maxWidthOrHeight = 1024
    const options = {
        maxSizeMB: maxSizeMB,
        maxWidthOrHeight: maxWidthOrHeight,
        useWebWorker: true,
        onProgress: (p: number) => {console.log(p, new Date().getSeconds(),)}
    };
    console.log(file.type, file.size, options); //   image/jpeg 87664 {maxSizeMB: 1, maxWidthOrHeight: 1024, useWebWorker: true, onProgress: [Function: onProgress]}
    const output = await imageCompression(file, options);
    console.log("output", output);
    return output
}
  1. The test causes the console to log console.log(file.type, file.size, options); // image/jpeg 87664 {maxSizeMB: 1, maxWidthOrHeight: 1024, useWebWorker: true, onProgress: [Function: onProgress]} log. I guess this demonstrates that the browser-image-compressor is working at some level.
  2. Then the console logs the onProgress: (p: number) => {console.log(p, new Date().getSeconds(),)} as 5 25, again suggesting the browser-image-compressor is working at some level.
  3. Then the test prints in the console as follows:

image

I've tried different jest.setTimeout(30000) timers, but they have no effect. The function just seems to stall.

**package.json:** ```json { "name": "here-i-go", "version": "0.2.37", "private": true, "dependencies": { "@material-ui/core": "^4.11.2", "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.57", "@sentry/react": "^6.3.1", "@sentry/tracing": "^6.3.1", "@svgr/webpack": "4.3.3", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", "@types/jest": "^26.0.24", "@types/lodash": "^4.14.177", "@types/node": "^14.17.5", "@types/react": "^17.0.14", "@types/react-dom": "^17.0.9", "axios": "^0.21.1", "babel-jest": "^24.9.0", "babel-plugin-named-asset-import": "^0.3.6", "babel-preset-react-app": "^9.1.2", "browser-image-compression": "^1.0.17", "camelcase": "^5.3.1", "case-sensitive-paths-webpack-plugin": "2.3.0", "chart.js": "^2.9.4", "chartjs-plugin-datalabels": "^0.7.0", "clsx": "^1.1.1", "cross-fetch": "^3.0.6", "css-loader": "3.4.2", "distinct-colors": "^3.0.0", "dotenv": "8.2.0", "dotenv-expand": "5.1.0", "eslint-loader": "^4.0.2", "file-loader": "4.3.0", "firebase": "^7.24.0", "firebase-admin": "^9.4.2", "firebase-functions": "^3.13.1", "fs-extra": "^8.1.0", "html-webpack-plugin": "4.0.0-beta.11", "identity-obj-proxy": "3.0.0", "jest": "24.9.0", "jest-environment-jsdom-fourteen": "1.0.1", "jest-resolve": "24.9.0", "jest-watch-typeahead": "0.4.2", "lodash": "^4.17.21", "luxon": "^1.25.0", "match-sorter": "^4.2.1", "mini-css-extract-plugin": "0.9.0", "moment": "^2.29.1", "netlify-cli": "^3.4.5", "notistack": "^1.0.2", "optimize-css-assets-webpack-plugin": "5.0.3", "pnp-webpack-plugin": "1.6.4", "postcss-flexbugs-fixes": "4.1.0", "postcss-loader": "3.0.0", "postcss-normalize": "8.0.1", "postcss-preset-env": "6.7.0", "postcss-safe-parser": "4.0.1", "react": "^17.0.1", "react-app-polyfill": "^1.0.6", "react-chartjs-2": "^2.11.1", "react-cookie": "^4.0.3", "react-dev-utils": "^11.0.4", "react-dom": "^17.0.1", "react-firebaseui": "^4.1.0", "react-ga": "^3.3.0", "react-hook-form": "^6.13.1", "react-instantsearch": "^6.8.2", "react-joyride": "^2.3.0", "react-moment": "^0.9.7", "react-numeric-input": "^2.2.3", "react-plain-carousel": "^1.3.1", "react-router-dom": "^5.2.0", "react-router-hash-link": "^2.4.0", "react-scripts": "^4.0.3", "resolve": "1.15.0", "resolve-url-loader": "^3.1.2", "sass-loader": "8.0.2", "semver": "6.3.0", "source-map-explorer": "^2.5.1", "style-loader": "0.23.1", "terser-webpack-plugin": "^2.3.8", "ts-pnp": "1.1.6", "url-loader": "2.3.0", "webpack-dev-server": "^3.11.2", "webpack-manifest-plugin": "2.2.0", "workbox-webpack-plugin": "4.3.1", "xyz": "^4.0.0" }, "devDependencies": { "@babel/core": "^7.14.6", "@babel/plugin-transform-runtime": "^7.9.0", "@babel/preset-env": "^7.14.7", "@babel/preset-react": "^7.14.5", "@babel/preset-typescript": "^7.9.0", "@babel/runtime": "^7.14.6", "@types/material-ui": "^0.21.8", "@types/react-router-dom": "^5.1.7", "@typescript-eslint/eslint-plugin": "^4.28.2", "@typescript-eslint/parser": "^4.28.2", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", "eslint": "^7.30.0", "eslint-config-react-app": "^6.0.0", "eslint-plugin-flowtype": "^5.8.0", "eslint-plugin-import": "^2.23.4", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-react": "^7.24.0", "eslint-plugin-react-hooks": "^4.2.0", "ts-loader": "^8.1.0", "ts-node": "^10.1.0", "typescript": "^3.9.10", "webpack": "^4.46.0" }, "scripts": { "start": "node scripts/start.js", "build": "node scripts/build.js && cat src/custom-service-worker.js >> build/service-worker.js", "test": "node scripts/test.js", "analyze": "source-map-explorer 'build/static/js/*.js'", }, "eslintConfig": { "extends": "react-app", "rules": { "@typescript-eslint/no-unused-vars": 1, "prefer-const": [ "warn" ], "@typescript-eslint/explicit-module-boundary-types": [ "warn" ] } }, "eslintIgnore": [ "**/*.js", "**/*.jsx" ], "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "jest": { "roots": [ "/src" ], "collectCoverageFrom": [ "src/**/*.{js,jsx,ts,tsx}", "!src/**/*.d.ts" ], "setupFiles": [ "react-app-polyfill/jsdom" ], "setupFilesAfterEnv": [ "/src/setupTests.js" ], "testMatch": [ "/src/**/__tests__/**/*.{js,jsx,ts,tsx}", "/src/**/*.{spec,test}.{js,jsx,ts,tsx}" ], "testEnvironment": "jest-environment-jsdom-fourteen", "transform": { "^.+\\.(js|jsx|ts|tsx)$": "/node_modules/babel-jest", "^.+\\.css$": "/config/jest/cssTransform.js", "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "/config/jest/fileTransform.js" }, "transformIgnorePatterns": [ "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$", "^.+\\.module\\.(css|sass|scss)$" ], "modulePaths": [], "moduleNameMapper": { "^react-native$": "react-native-web", "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy" }, "moduleFileExtensions": [ "web.js", "js", "web.ts", "ts", "web.tsx", "tsx", "json", "web.jsx", "jsx", "node" ], "watchPlugins": [ "jest-watch-typeahead/filename", "jest-watch-typeahead/testname" ] }, "babel": { "presets": [ "@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript" ], "plugins": [ "@babel/plugin-proposal-nullish-coalescing-operator", "@babel/plugin-proposal-optional-chaining", [ "@babel/plugin-transform-runtime", { "regenerator": true } ] ] } } ```
Donaldcwl commented 2 years ago

I guess there are some browser features that were not included in your testing environment such as window.navigator, window.createImageBitmap, window.FileReader, window.File, Image, HTMLCanvasElement etc. I just have a quick look at your jest config, "jest-environment-jsdom-fourteen" seems to be deprecated, you may try to upgrade the jsdom. Hope it helps :)

jimmythecode commented 2 years ago

Thanks for this. I've spent some hours trying to understand how jest works with the dom, but I think I'm actually a good few weeks of practice away before I really get it. I've created a new CRA app with typescript and it's predictably giving me the same errors with the following /package.json set up:

  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "@types/jest": "^26.0.15",
    "@types/node": "^12.0.0",
    "@types/react": "^17.0.0",
    "@types/react-dom": "^17.0.0",
    "browser-image-compression": "^1.0.17",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3",
    "typescript": "^4.1.2",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "start": "react-scripts start",

If it's a relatively simple fix, do you know what I'd need to do to make a simple test (eg the original example I gave above) work? Is it a case of needing to mock each method on the window object, or is there a quick fix?

Donaldcwl commented 2 years ago

You may way to have a look at how the test set up of this library here: https://github.com/Donaldcwl/browser-image-compression/blob/master/test/setup_jsdom.js

If you still can't set it up, please share your CRA with me.