mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-11-04 04:44:31 +01:00 
			
		
		
		
	Write integration tests (#719)
* Scaffold a simple test case for debugging * Set up Jest environment that works with React - Install and set up react-testing-library - "Unignore" roughjs and browser-nativejs transformations - Separate App component from ReactDOM * Write first passing test - Mock canvas - Remove App file and mount/unmount ReactDOM on import * Add tests for drag create behavior * Fix comments in dragCreate * Pin jest-mock-canvas dependency * Remove dependency range for testing library * Add tests for multi point mode and selection element * Fix all tests due to decrease in updates to canvas when changing tools * Disable state updates if component is unmounted - Remove all event listeners - Disable storing scene in state if component is unmounted * Add tests for move and element selection * Merge branch 'master' into add-integration-tests * Add tests for resizing rectangle * move unmounted check to syncActionResult method * Use a custom test renderer instead of default testing-library functions * Add custom query for selecting tools * move files around Co-authored-by: David Luzar <luzar.david@gmail.com>
This commit is contained in:
		
							
								
								
									
										268
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										268
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1292,6 +1292,12 @@
 | 
			
		||||
        "any-observable": "^0.3.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@sheerun/mutationobserver-shim": {
 | 
			
		||||
      "version": "0.3.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz",
 | 
			
		||||
      "integrity": "sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "@svgr/babel-plugin-add-jsx-attribute": {
 | 
			
		||||
      "version": "4.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
 | 
			
		||||
@@ -1401,6 +1407,189 @@
 | 
			
		||||
        "loader-utils": "^1.2.3"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@testing-library/dom": {
 | 
			
		||||
      "version": "6.12.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-6.12.2.tgz",
 | 
			
		||||
      "integrity": "sha512-KCnvHra5fV+wDxg3wJObGvZFxq7v1DJt829GNFLuRDjKxVNc/B5AdsylNF5PMHFbWMXDsHwM26d2NZcZO9KjbQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@babel/runtime": "^7.6.2",
 | 
			
		||||
        "@sheerun/mutationobserver-shim": "^0.3.2",
 | 
			
		||||
        "@types/testing-library__dom": "^6.0.0",
 | 
			
		||||
        "aria-query": "3.0.0",
 | 
			
		||||
        "pretty-format": "^24.9.0",
 | 
			
		||||
        "wait-for-expect": "^3.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@testing-library/jest-dom": {
 | 
			
		||||
      "version": "5.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.1.1.tgz",
 | 
			
		||||
      "integrity": "sha512-7xnmBFcUmmUVAUhFiZ/u3CxFh1e46THAwra4SiiKNCW4By26RedCRwEk0rtleFPZG0wlTSNOKDvJjWYy93dp0w==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@babel/runtime": "^7.8.3",
 | 
			
		||||
        "@types/testing-library__jest-dom": "^5.0.0",
 | 
			
		||||
        "chalk": "^3.0.0",
 | 
			
		||||
        "css": "^2.2.4",
 | 
			
		||||
        "css.escape": "^1.5.1",
 | 
			
		||||
        "jest-diff": "^25.1.0",
 | 
			
		||||
        "jest-matcher-utils": "^25.1.0",
 | 
			
		||||
        "lodash": "^4.17.15",
 | 
			
		||||
        "pretty-format": "^25.1.0",
 | 
			
		||||
        "redent": "^3.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@jest/types": {
 | 
			
		||||
          "version": "25.1.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz",
 | 
			
		||||
          "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "@types/istanbul-lib-coverage": "^2.0.0",
 | 
			
		||||
            "@types/istanbul-reports": "^1.1.1",
 | 
			
		||||
            "@types/yargs": "^15.0.0",
 | 
			
		||||
            "chalk": "^3.0.0"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "@types/yargs": {
 | 
			
		||||
          "version": "15.0.3",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.3.tgz",
 | 
			
		||||
          "integrity": "sha512-XCMQRK6kfpNBixHLyHUsGmXrpEmFFxzMrcnSXFMziHd8CoNJo8l16FkHyQq4x+xbM7E2XL83/O78OD8u+iZTdQ==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "@types/yargs-parser": "*"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "ansi-styles": {
 | 
			
		||||
          "version": "4.2.1",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
 | 
			
		||||
          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "@types/color-name": "^1.1.1",
 | 
			
		||||
            "color-convert": "^2.0.1"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "chalk": {
 | 
			
		||||
          "version": "3.0.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
 | 
			
		||||
          "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "ansi-styles": "^4.1.0",
 | 
			
		||||
            "supports-color": "^7.1.0"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "color-convert": {
 | 
			
		||||
          "version": "2.0.1",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
 | 
			
		||||
          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "color-name": "~1.1.4"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "color-name": {
 | 
			
		||||
          "version": "1.1.4",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
 | 
			
		||||
          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
 | 
			
		||||
          "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "diff-sequences": {
 | 
			
		||||
          "version": "25.1.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz",
 | 
			
		||||
          "integrity": "sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==",
 | 
			
		||||
          "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "has-flag": {
 | 
			
		||||
          "version": "4.0.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
 | 
			
		||||
          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
 | 
			
		||||
          "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "jest-diff": {
 | 
			
		||||
          "version": "25.1.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.1.0.tgz",
 | 
			
		||||
          "integrity": "sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "chalk": "^3.0.0",
 | 
			
		||||
            "diff-sequences": "^25.1.0",
 | 
			
		||||
            "jest-get-type": "^25.1.0",
 | 
			
		||||
            "pretty-format": "^25.1.0"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "jest-get-type": {
 | 
			
		||||
          "version": "25.1.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz",
 | 
			
		||||
          "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==",
 | 
			
		||||
          "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "jest-matcher-utils": {
 | 
			
		||||
          "version": "25.1.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz",
 | 
			
		||||
          "integrity": "sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "chalk": "^3.0.0",
 | 
			
		||||
            "jest-diff": "^25.1.0",
 | 
			
		||||
            "jest-get-type": "^25.1.0",
 | 
			
		||||
            "pretty-format": "^25.1.0"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "pretty-format": {
 | 
			
		||||
          "version": "25.1.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz",
 | 
			
		||||
          "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "@jest/types": "^25.1.0",
 | 
			
		||||
            "ansi-regex": "^5.0.0",
 | 
			
		||||
            "ansi-styles": "^4.0.0",
 | 
			
		||||
            "react-is": "^16.12.0"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "redent": {
 | 
			
		||||
          "version": "3.0.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
 | 
			
		||||
          "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "indent-string": "^4.0.0",
 | 
			
		||||
            "strip-indent": "^3.0.0"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "strip-indent": {
 | 
			
		||||
          "version": "3.0.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
 | 
			
		||||
          "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "min-indent": "^1.0.0"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "supports-color": {
 | 
			
		||||
          "version": "7.1.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
 | 
			
		||||
          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
 | 
			
		||||
          "dev": true,
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "has-flag": "^4.0.0"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@testing-library/react": {
 | 
			
		||||
      "version": "9.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-9.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-XdhDWkI4GktUPsz0AYyeQ8M9qS/JFie06kcSnUVcpgOwFjAu9vhwR83qBl+lw9yZWkbECjL8Hd+n5hH6C0oWqg==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@babel/runtime": "^7.7.6",
 | 
			
		||||
        "@testing-library/dom": "^6.11.0",
 | 
			
		||||
        "@types/testing-library__react": "^9.1.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@types/babel__core": {
 | 
			
		||||
      "version": "7.1.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz",
 | 
			
		||||
@@ -1668,6 +1857,34 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw=="
 | 
			
		||||
    },
 | 
			
		||||
    "@types/testing-library__dom": {
 | 
			
		||||
      "version": "6.12.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.12.0.tgz",
 | 
			
		||||
      "integrity": "sha512-PQ/gzABzc53T68RldZ/sJHKCihtP9ofU8XIgOk+H7tlfoCRdg9mqICio5Fo8j3Z8wo+pOfuDsuPprWsn3YtVmA==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "pretty-format": "^24.3.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@types/testing-library__jest-dom": {
 | 
			
		||||
      "version": "5.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-GiPXQBVF9O4DG9cssD2d266vozBJvC5Tnv6aeH5ujgYJgys1DYm9AFCz7YC+STR5ksGxq3zCt+yP8T1wbk2DFg==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@types/jest": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@types/testing-library__react": {
 | 
			
		||||
      "version": "9.1.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/testing-library__react/-/testing-library__react-9.1.2.tgz",
 | 
			
		||||
      "integrity": "sha512-CYaMqrswQ+cJACy268jsLAw355DZtPZGt3Jwmmotlcu8O/tkoXBI6AeZ84oZBJsIsesozPKzWzmv/0TIU+1E9Q==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@types/react-dom": "*",
 | 
			
		||||
        "@types/testing-library__dom": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@types/yargs": {
 | 
			
		||||
      "version": "13.0.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz",
 | 
			
		||||
@@ -3863,6 +4080,12 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz",
 | 
			
		||||
      "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw=="
 | 
			
		||||
    },
 | 
			
		||||
    "css.escape": {
 | 
			
		||||
      "version": "1.5.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
 | 
			
		||||
      "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "cssdb": {
 | 
			
		||||
      "version": "4.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz",
 | 
			
		||||
@@ -3873,6 +4096,12 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
 | 
			
		||||
    },
 | 
			
		||||
    "cssfontparser": {
 | 
			
		||||
      "version": "1.2.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz",
 | 
			
		||||
      "integrity": "sha1-9AIvyPlwDGgCnVQghK+69CWj8+M=",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "cssnano": {
 | 
			
		||||
      "version": "4.1.10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz",
 | 
			
		||||
@@ -7154,6 +7383,16 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "jest-canvas-mock": {
 | 
			
		||||
      "version": "2.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-DcJdchb7eWFZkt6pvyceWWnu3lsp5QWbUeXiKgEMhwB3sMm5qHM1GQhDajvJgBeiYpgKcojbzZ53d/nz6tXvJw==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "cssfontparser": "^1.2.1",
 | 
			
		||||
        "parse-color": "^1.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "jest-changed-files": {
 | 
			
		||||
      "version": "24.9.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz",
 | 
			
		||||
@@ -9390,6 +9629,12 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
 | 
			
		||||
    },
 | 
			
		||||
    "min-indent": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY=",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "mini-css-extract-plugin": {
 | 
			
		||||
      "version": "0.9.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz",
 | 
			
		||||
@@ -10359,6 +10604,23 @@
 | 
			
		||||
        "safe-buffer": "^5.1.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "parse-color": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha1-e3SLlag/A/FqlPU15S1/PZRlhhk=",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "color-convert": "~0.5.0"
 | 
			
		||||
      },
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "color-convert": {
 | 
			
		||||
          "version": "0.5.3",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
 | 
			
		||||
          "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=",
 | 
			
		||||
          "dev": true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "parse-json": {
 | 
			
		||||
      "version": "4.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
 | 
			
		||||
@@ -14952,6 +15214,12 @@
 | 
			
		||||
        "xml-name-validator": "^3.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "wait-for-expect": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-3Ha7lu+zshEG/CeHdcpmQsZnnZpPj/UsG3DuKO8FskjuDbkx3jE3845H+CuwZjA2YWYDfKMU2KhnCaXMLd3wVw==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "walker": {
 | 
			
		||||
      "version": "1.0.7",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz",
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,8 @@
 | 
			
		||||
  },
 | 
			
		||||
  "description": "",
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@testing-library/jest-dom": "5.1.1",
 | 
			
		||||
    "@testing-library/react": "9.4.0",
 | 
			
		||||
    "@types/jest": "25.1.2",
 | 
			
		||||
    "@types/nanoid": "2.1.0",
 | 
			
		||||
    "@types/react": "16.9.19",
 | 
			
		||||
@@ -24,12 +26,18 @@
 | 
			
		||||
    "eslint-config-prettier": "6.10.0",
 | 
			
		||||
    "eslint-plugin-prettier": "3.1.2",
 | 
			
		||||
    "husky": "4.2.1",
 | 
			
		||||
    "jest-canvas-mock": "2.2.0",
 | 
			
		||||
    "lint-staged": "10.0.7",
 | 
			
		||||
    "node-sass": "4.13.1",
 | 
			
		||||
    "prettier": "1.19.1",
 | 
			
		||||
    "rewire": "4.0.1",
 | 
			
		||||
    "typescript": "3.7.5"
 | 
			
		||||
  },
 | 
			
		||||
  "jest": {
 | 
			
		||||
    "transformIgnorePatterns": [
 | 
			
		||||
      "node_modules/(?!(roughjs|browser-nativefs)/)"
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "eslintConfig": {
 | 
			
		||||
    "extends": [
 | 
			
		||||
      "prettier",
 | 
			
		||||
 
 | 
			
		||||
@@ -475,6 +475,9 @@ export class App extends React.Component<any, AppState> {
 | 
			
		||||
    res: ActionResult,
 | 
			
		||||
    commitToHistory: boolean = true,
 | 
			
		||||
  ) => {
 | 
			
		||||
    if (this.unmounted) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (res.elements) {
 | 
			
		||||
      elements = res.elements;
 | 
			
		||||
      if (commitToHistory) {
 | 
			
		||||
@@ -515,6 +518,11 @@ export class App extends React.Component<any, AppState> {
 | 
			
		||||
    this.saveDebounced.flush();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private disableEvent: EventHandlerNonNull = e => {
 | 
			
		||||
    e.preventDefault();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private unmounted = false;
 | 
			
		||||
  public async componentDidMount() {
 | 
			
		||||
    document.addEventListener("copy", this.onCopy);
 | 
			
		||||
    document.addEventListener("paste", this.pasteFromClipboard);
 | 
			
		||||
@@ -526,28 +534,32 @@ export class App extends React.Component<any, AppState> {
 | 
			
		||||
    window.addEventListener("resize", this.onResize, false);
 | 
			
		||||
    window.addEventListener("unload", this.onUnload, false);
 | 
			
		||||
    window.addEventListener("blur", this.onUnload, false);
 | 
			
		||||
    window.addEventListener("dragover", e => e.preventDefault(), false);
 | 
			
		||||
    window.addEventListener("drop", e => e.preventDefault(), false);
 | 
			
		||||
    window.addEventListener("dragover", this.disableEvent, false);
 | 
			
		||||
    window.addEventListener("drop", this.disableEvent, false);
 | 
			
		||||
 | 
			
		||||
    const searchParams = new URLSearchParams(window.location.search);
 | 
			
		||||
    const id = searchParams.get("id");
 | 
			
		||||
 | 
			
		||||
    if (id) {
 | 
			
		||||
      // Backwards compatibility with legacy url format
 | 
			
		||||
      this.syncActionResult(await loadScene(id));
 | 
			
		||||
      const scene = await loadScene(id);
 | 
			
		||||
      this.syncActionResult(scene);
 | 
			
		||||
    } else {
 | 
			
		||||
      const match = window.location.hash.match(
 | 
			
		||||
        /^#json=([0-9]+),([a-zA-Z0-9_-]+)$/,
 | 
			
		||||
      );
 | 
			
		||||
      if (match) {
 | 
			
		||||
        this.syncActionResult(await loadScene(match[1], match[2]));
 | 
			
		||||
        const scene = await loadScene(match[1], match[2]);
 | 
			
		||||
        this.syncActionResult(scene);
 | 
			
		||||
      } else {
 | 
			
		||||
        this.syncActionResult(await loadScene(null));
 | 
			
		||||
        const scene = await loadScene(null);
 | 
			
		||||
        this.syncActionResult(scene);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public componentWillUnmount() {
 | 
			
		||||
    this.unmounted = true;
 | 
			
		||||
    document.removeEventListener("copy", this.onCopy);
 | 
			
		||||
    document.removeEventListener("paste", this.pasteFromClipboard);
 | 
			
		||||
    document.removeEventListener("cut", this.onCut);
 | 
			
		||||
@@ -558,9 +570,12 @@ export class App extends React.Component<any, AppState> {
 | 
			
		||||
      this.updateCurrentCursorPosition,
 | 
			
		||||
      false,
 | 
			
		||||
    );
 | 
			
		||||
    document.removeEventListener("keyup", this.onKeyUp);
 | 
			
		||||
    window.removeEventListener("resize", this.onResize, false);
 | 
			
		||||
    window.removeEventListener("unload", this.onUnload, false);
 | 
			
		||||
    window.removeEventListener("blur", this.onUnload, false);
 | 
			
		||||
    window.removeEventListener("dragover", this.disableEvent, false);
 | 
			
		||||
    window.removeEventListener("drop", this.disableEvent, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public state: AppState = getDefaultAppState();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								src/setupTests.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/setupTests.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
import "@testing-library/jest-dom";
 | 
			
		||||
import "jest-canvas-mock";
 | 
			
		||||
 | 
			
		||||
// ReactDOM is located inside index.tsx file
 | 
			
		||||
// as a result, we need a place for it to render into
 | 
			
		||||
const element = document.createElement("div");
 | 
			
		||||
element.id = "root";
 | 
			
		||||
document.body.appendChild(element);
 | 
			
		||||
							
								
								
									
										277
									
								
								src/tests/dragCreate.test.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								src/tests/dragCreate.test.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,277 @@
 | 
			
		||||
import React from "react";
 | 
			
		||||
import ReactDOM from "react-dom";
 | 
			
		||||
import { App } from "../index";
 | 
			
		||||
import * as Renderer from "../renderer/renderScene";
 | 
			
		||||
import { KEYS } from "../keys";
 | 
			
		||||
import { render, fireEvent } from "./test-utils";
 | 
			
		||||
 | 
			
		||||
// Unmount ReactDOM from root
 | 
			
		||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
 | 
			
		||||
 | 
			
		||||
const renderScene = jest.spyOn(Renderer, "renderScene");
 | 
			
		||||
beforeEach(() => {
 | 
			
		||||
  localStorage.clear();
 | 
			
		||||
  renderScene.mockClear();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("add element to the scene when mouse dragging long enough", () => {
 | 
			
		||||
  it("rectangle", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("rectangle");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    // start from (30, 20)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
 | 
			
		||||
    // move to (60,70)
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
 | 
			
		||||
    // finish (position does not matter)
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
    expect(renderScene.mock.calls[3][1]).toBeNull();
 | 
			
		||||
    const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect(elements[0].type).toEqual("rectangle");
 | 
			
		||||
    expect(elements[0].x).toEqual(30);
 | 
			
		||||
    expect(elements[0].y).toEqual(20);
 | 
			
		||||
    expect(elements[0].width).toEqual(30); // 60 - 30
 | 
			
		||||
    expect(elements[0].height).toEqual(50); // 70 - 20
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("ellipse", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("ellipse");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    // start from (30, 20)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
 | 
			
		||||
    // move to (60,70)
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
 | 
			
		||||
    // finish (position does not matter)
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
    expect(renderScene.mock.calls[3][1]).toBeNull();
 | 
			
		||||
 | 
			
		||||
    const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect(elements[0].type).toEqual("ellipse");
 | 
			
		||||
    expect(elements[0].x).toEqual(30);
 | 
			
		||||
    expect(elements[0].y).toEqual(20);
 | 
			
		||||
    expect(elements[0].width).toEqual(30); // 60 - 30
 | 
			
		||||
    expect(elements[0].height).toEqual(50); // 70 - 20
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("diamond", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("diamond");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    // start from (30, 20)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
 | 
			
		||||
    // move to (60,70)
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
 | 
			
		||||
    // finish (position does not matter)
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
    expect(renderScene.mock.calls[3][1]).toBeNull();
 | 
			
		||||
 | 
			
		||||
    const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect(elements[0].type).toEqual("diamond");
 | 
			
		||||
    expect(elements[0].x).toEqual(30);
 | 
			
		||||
    expect(elements[0].y).toEqual(20);
 | 
			
		||||
    expect(elements[0].width).toEqual(30); // 60 - 30
 | 
			
		||||
    expect(elements[0].height).toEqual(50); // 70 - 20
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("arrow", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("arrow");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    // start from (30, 20)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
 | 
			
		||||
    // move to (60,70)
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
 | 
			
		||||
    // finish (position does not matter)
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
    expect(renderScene.mock.calls[3][1]).toBeNull();
 | 
			
		||||
    const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect(elements[0].type).toEqual("arrow");
 | 
			
		||||
    expect(elements[0].x).toEqual(30);
 | 
			
		||||
    expect(elements[0].y).toEqual(20);
 | 
			
		||||
    expect(elements[0].points.length).toEqual(2);
 | 
			
		||||
    expect(elements[0].points[0]).toEqual([0, 0]);
 | 
			
		||||
    expect(elements[0].points[1]).toEqual([30, 50]); // (60 - 30, 70 - 20)
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("line", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("line");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    // start from (30, 20)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
 | 
			
		||||
    // move to (60,70)
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
 | 
			
		||||
    // finish (position does not matter)
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
    expect(renderScene.mock.calls[3][1]).toBeNull();
 | 
			
		||||
    const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect(elements[0].type).toEqual("line");
 | 
			
		||||
    expect(elements[0].x).toEqual(30);
 | 
			
		||||
    expect(elements[0].y).toEqual(20);
 | 
			
		||||
    expect(elements[0].points.length).toEqual(2);
 | 
			
		||||
    expect(elements[0].points[0]).toEqual([0, 0]);
 | 
			
		||||
    expect(elements[0].points[1]).toEqual([30, 50]); // (60 - 30, 70 - 20)
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("do not add element to the scene if size is too small", () => {
 | 
			
		||||
  it("rectangle", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("rectangle");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    // start from (30, 20)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
 | 
			
		||||
    // finish (position does not matter)
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(3);
 | 
			
		||||
    expect(renderScene.mock.calls[2][1]).toBeNull();
 | 
			
		||||
    const elements = renderScene.mock.calls[2][0];
 | 
			
		||||
 | 
			
		||||
    expect(elements.length).toEqual(0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("ellipse", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("ellipse");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    // start from (30, 20)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
 | 
			
		||||
    // finish (position does not matter)
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(3);
 | 
			
		||||
    expect(renderScene.mock.calls[2][1]).toBeNull();
 | 
			
		||||
    const elements = renderScene.mock.calls[2][0];
 | 
			
		||||
 | 
			
		||||
    expect(elements.length).toEqual(0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("diamond", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("diamond");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    // start from (30, 20)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
 | 
			
		||||
    // finish (position does not matter)
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(3);
 | 
			
		||||
    expect(renderScene.mock.calls[2][1]).toBeNull();
 | 
			
		||||
    const elements = renderScene.mock.calls[2][0];
 | 
			
		||||
 | 
			
		||||
    expect(elements.length).toEqual(0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("arrow", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("arrow");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    // start from (30, 20)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
 | 
			
		||||
    // finish (position does not matter)
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    // we need to finalize it because arrows and lines enter multi-mode
 | 
			
		||||
    fireEvent.keyDown(document, { key: KEYS.ENTER });
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
    expect(renderScene.mock.calls[3][1]).toBeNull();
 | 
			
		||||
    const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
 | 
			
		||||
    expect(elements.length).toEqual(0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("line", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("line");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    // start from (30, 20)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
 | 
			
		||||
    // finish (position does not matter)
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    // we need to finalize it because arrows and lines enter multi-mode
 | 
			
		||||
    fireEvent.keyDown(document, { key: KEYS.ENTER });
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
    expect(renderScene.mock.calls[3][1]).toBeNull();
 | 
			
		||||
    const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
 | 
			
		||||
    expect(elements.length).toEqual(0);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										88
									
								
								src/tests/move.test.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/tests/move.test.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
import React from "react";
 | 
			
		||||
import ReactDOM from "react-dom";
 | 
			
		||||
import { render, fireEvent } from "./test-utils";
 | 
			
		||||
import { App } from "../index";
 | 
			
		||||
import * as Renderer from "../renderer/renderScene";
 | 
			
		||||
 | 
			
		||||
// Unmount ReactDOM from root
 | 
			
		||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
 | 
			
		||||
 | 
			
		||||
const renderScene = jest.spyOn(Renderer, "renderScene");
 | 
			
		||||
beforeEach(() => {
 | 
			
		||||
  localStorage.clear();
 | 
			
		||||
  renderScene.mockClear();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("move element", () => {
 | 
			
		||||
  it("rectangle", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
      // create element
 | 
			
		||||
      const tool = getByToolName("rectangle");
 | 
			
		||||
      fireEvent.click(tool);
 | 
			
		||||
      fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
      fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
      fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
      expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
      const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
      const selectionElement = renderScene.mock.calls[3][1];
 | 
			
		||||
      expect(selectionElement).toBeNull();
 | 
			
		||||
      expect(elements.length).toEqual(1);
 | 
			
		||||
      expect(elements[0].isSelected).toBeTruthy();
 | 
			
		||||
      expect([elements[0].x, elements[0].y]).toEqual([30, 20]);
 | 
			
		||||
 | 
			
		||||
      renderScene.mockClear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 50, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 20, clientY: 40 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(3);
 | 
			
		||||
    const elements = renderScene.mock.calls[2][0];
 | 
			
		||||
    expect(renderScene.mock.calls[2][1]).toBeNull();
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect([elements[0].x, elements[0].y]).toEqual([0, 40]);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("duplicate element on move when ALT is clicked", () => {
 | 
			
		||||
  it("rectangle", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
      // create element
 | 
			
		||||
      const tool = getByToolName("rectangle");
 | 
			
		||||
      fireEvent.click(tool);
 | 
			
		||||
      fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
      fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
      fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
      expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
      const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
      const selectionElement = renderScene.mock.calls[3][1];
 | 
			
		||||
      expect(selectionElement).toBeNull();
 | 
			
		||||
      expect(elements.length).toEqual(1);
 | 
			
		||||
      expect(elements[0].isSelected).toBeTruthy();
 | 
			
		||||
      expect([elements[0].x, elements[0].y]).toEqual([30, 20]);
 | 
			
		||||
 | 
			
		||||
      renderScene.mockClear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 50, clientY: 20, altKey: true });
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 20, clientY: 40 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(3);
 | 
			
		||||
    const elements = renderScene.mock.calls[2][0];
 | 
			
		||||
    expect(renderScene.mock.calls[2][1]).toBeNull();
 | 
			
		||||
    expect(elements.length).toEqual(2);
 | 
			
		||||
    // previous element should stay intact
 | 
			
		||||
    expect([elements[0].x, elements[0].y]).toEqual([30, 20]);
 | 
			
		||||
    expect([elements[1].x, elements[1].y]).toEqual([0, 40]);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										140
									
								
								src/tests/multiPointCreate.test.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/tests/multiPointCreate.test.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
			
		||||
import React from "react";
 | 
			
		||||
import ReactDOM from "react-dom";
 | 
			
		||||
import { render, fireEvent } from "./test-utils";
 | 
			
		||||
import { App } from "../index";
 | 
			
		||||
import * as Renderer from "../renderer/renderScene";
 | 
			
		||||
import { KEYS } from "../keys";
 | 
			
		||||
 | 
			
		||||
// Unmount ReactDOM from root
 | 
			
		||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
 | 
			
		||||
 | 
			
		||||
const renderScene = jest.spyOn(Renderer, "renderScene");
 | 
			
		||||
beforeEach(() => {
 | 
			
		||||
  localStorage.clear();
 | 
			
		||||
  renderScene.mockClear();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("remove shape in non linear elements", () => {
 | 
			
		||||
  it("rectangle", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("rectangle");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseUp(canvas, { clientX: 30, clientY: 30 });
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(3);
 | 
			
		||||
    const elements = renderScene.mock.calls[2][0];
 | 
			
		||||
    expect(elements.length).toEqual(0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("ellipse", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("ellipse");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseUp(canvas, { clientX: 30, clientY: 30 });
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(3);
 | 
			
		||||
    const elements = renderScene.mock.calls[2][0];
 | 
			
		||||
    expect(elements.length).toEqual(0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("diamond", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("diamond");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseUp(canvas, { clientX: 30, clientY: 30 });
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(3);
 | 
			
		||||
    const elements = renderScene.mock.calls[2][0];
 | 
			
		||||
    expect(elements.length).toEqual(0);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("multi point mode in linear elements", () => {
 | 
			
		||||
  it("arrow", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("arrow");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    // first point is added on mouse down
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 30 });
 | 
			
		||||
 | 
			
		||||
    // second point, enable multi point
 | 
			
		||||
    fireEvent.mouseUp(canvas, { clientX: 30, clientY: 30 });
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 50, clientY: 60 });
 | 
			
		||||
 | 
			
		||||
    // third point
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 50, clientY: 60 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 100, clientY: 140 });
 | 
			
		||||
 | 
			
		||||
    // done
 | 
			
		||||
    fireEvent.mouseDown(canvas);
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
    fireEvent.keyDown(document, { key: KEYS.ENTER });
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(8);
 | 
			
		||||
    const elements = renderScene.mock.calls[7][0];
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
 | 
			
		||||
    expect(elements[0].type).toEqual("arrow");
 | 
			
		||||
    expect(elements[0].x).toEqual(30);
 | 
			
		||||
    expect(elements[0].y).toEqual(30);
 | 
			
		||||
    expect(elements[0].points).toEqual([
 | 
			
		||||
      [0, 0],
 | 
			
		||||
      [20, 30],
 | 
			
		||||
      [70, 110],
 | 
			
		||||
    ]);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("line", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("line");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    // first point is added on mouse down
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 30, clientY: 30 });
 | 
			
		||||
 | 
			
		||||
    // second point, enable multi point
 | 
			
		||||
    fireEvent.mouseUp(canvas, { clientX: 30, clientY: 30 });
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 50, clientY: 60 });
 | 
			
		||||
 | 
			
		||||
    // third point
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 50, clientY: 60 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 100, clientY: 140 });
 | 
			
		||||
 | 
			
		||||
    // done
 | 
			
		||||
    fireEvent.mouseDown(canvas);
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
    fireEvent.keyDown(document, { key: KEYS.ENTER });
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(8);
 | 
			
		||||
    const elements = renderScene.mock.calls[7][0];
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
 | 
			
		||||
    expect(elements[0].type).toEqual("line");
 | 
			
		||||
    expect(elements[0].x).toEqual(30);
 | 
			
		||||
    expect(elements[0].y).toEqual(30);
 | 
			
		||||
    expect(elements[0].points).toEqual([
 | 
			
		||||
      [0, 0],
 | 
			
		||||
      [20, 30],
 | 
			
		||||
      [70, 110],
 | 
			
		||||
    ]);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										32
									
								
								src/tests/queries/toolQueries.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/tests/queries/toolQueries.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
import { queries, buildQueries } from "@testing-library/react";
 | 
			
		||||
 | 
			
		||||
const _getAllByToolName = (container: HTMLElement, tool: string) => {
 | 
			
		||||
  const toolMap: { [propKey: string]: string } = {
 | 
			
		||||
    selection: "Selection — S, 1",
 | 
			
		||||
    rectangle: "Rectangle — R, 2",
 | 
			
		||||
    diamond: "Diamond — D, 3",
 | 
			
		||||
    ellipse: "Ellipse — E, 4",
 | 
			
		||||
    arrow: "Arrow — A, 5",
 | 
			
		||||
    line: "Line — L, 6",
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const toolTitle = toolMap[tool as string];
 | 
			
		||||
  return queries.getAllByTitle(container, toolTitle);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getMultipleError = (c: any, tool: any) =>
 | 
			
		||||
  `Found multiple elements with tool name: ${tool}`;
 | 
			
		||||
const getMissingError = (c: any, tool: any) =>
 | 
			
		||||
  `Unable to find an element with tool name: ${tool}`;
 | 
			
		||||
 | 
			
		||||
export const [
 | 
			
		||||
  queryByToolName,
 | 
			
		||||
  getAllByToolName,
 | 
			
		||||
  getByToolName,
 | 
			
		||||
  findAllByToolName,
 | 
			
		||||
  findByToolName,
 | 
			
		||||
] = buildQueries<string[]>(
 | 
			
		||||
  _getAllByToolName,
 | 
			
		||||
  getMultipleError,
 | 
			
		||||
  getMissingError,
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										98
									
								
								src/tests/resize.test.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/tests/resize.test.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
import React from "react";
 | 
			
		||||
import ReactDOM from "react-dom";
 | 
			
		||||
import { render, fireEvent } from "./test-utils";
 | 
			
		||||
import { App } from "../index";
 | 
			
		||||
import * as Renderer from "../renderer/renderScene";
 | 
			
		||||
 | 
			
		||||
// Unmount ReactDOM from root
 | 
			
		||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
 | 
			
		||||
 | 
			
		||||
const renderScene = jest.spyOn(Renderer, "renderScene");
 | 
			
		||||
beforeEach(() => {
 | 
			
		||||
  localStorage.clear();
 | 
			
		||||
  renderScene.mockClear();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("resize element", () => {
 | 
			
		||||
  it("rectangle", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
      // create element
 | 
			
		||||
      const tool = getByToolName("rectangle");
 | 
			
		||||
      fireEvent.click(tool);
 | 
			
		||||
      fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
      fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
      fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
      expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
      const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
      const selectionElement = renderScene.mock.calls[3][1];
 | 
			
		||||
      expect(selectionElement).toBeNull();
 | 
			
		||||
      expect(elements.length).toEqual(1);
 | 
			
		||||
      expect(elements[0].isSelected).toBeTruthy();
 | 
			
		||||
      expect([elements[0].x, elements[0].y]).toEqual([30, 20]);
 | 
			
		||||
 | 
			
		||||
      renderScene.mockClear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // select the element first
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 50, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    // select a handler rectangle (top-left)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 21, clientY: 13 });
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 20, clientY: 40 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(5);
 | 
			
		||||
    const elements = renderScene.mock.calls[4][0];
 | 
			
		||||
    expect(renderScene.mock.calls[4][1]).toBeNull();
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect([elements[0].x, elements[0].y]).toEqual([29, 47]);
 | 
			
		||||
    expect([elements[0].width, elements[0].height]).toEqual([31, 23]);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("resize element with aspect ratio when SHIFT is clicked", () => {
 | 
			
		||||
  it("rectangle", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
      // create element
 | 
			
		||||
      const tool = getByToolName("rectangle");
 | 
			
		||||
      fireEvent.click(tool);
 | 
			
		||||
      fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
      fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
      fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
      expect(renderScene).toHaveBeenCalledTimes(4);
 | 
			
		||||
      const elements = renderScene.mock.calls[3][0];
 | 
			
		||||
      const selectionElement = renderScene.mock.calls[3][1];
 | 
			
		||||
      expect(selectionElement).toBeNull();
 | 
			
		||||
      expect(elements.length).toEqual(1);
 | 
			
		||||
      expect(elements[0].isSelected).toBeTruthy();
 | 
			
		||||
      expect([elements[0].x, elements[0].y]).toEqual([30, 20]);
 | 
			
		||||
 | 
			
		||||
      renderScene.mockClear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // select the element first
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 50, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    // select a handler rectangle (top-left)
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 21, clientY: 13 });
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 20, clientY: 40, shiftKey: true });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(5);
 | 
			
		||||
    const elements = renderScene.mock.calls[4][0];
 | 
			
		||||
    expect(renderScene.mock.calls[4][1]).toBeNull();
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect([elements[0].x, elements[0].y]).toEqual([29, 39]);
 | 
			
		||||
    expect([elements[0].width, elements[0].height]).toEqual([31, 31]);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										211
									
								
								src/tests/selection.test.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/tests/selection.test.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
			
		||||
import React from "react";
 | 
			
		||||
import ReactDOM from "react-dom";
 | 
			
		||||
import { render, fireEvent } from "./test-utils";
 | 
			
		||||
import { App } from "../index";
 | 
			
		||||
import * as Renderer from "../renderer/renderScene";
 | 
			
		||||
import { KEYS } from "../keys";
 | 
			
		||||
 | 
			
		||||
// Unmount ReactDOM from root
 | 
			
		||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
 | 
			
		||||
 | 
			
		||||
const renderScene = jest.spyOn(Renderer, "renderScene");
 | 
			
		||||
beforeEach(() => {
 | 
			
		||||
  localStorage.clear();
 | 
			
		||||
  renderScene.mockClear();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("selection element", () => {
 | 
			
		||||
  it("create selection element on mouse down", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("selection");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 60, clientY: 100 });
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(1);
 | 
			
		||||
    const selectionElement = renderScene.mock.calls[0][1]!;
 | 
			
		||||
    expect(selectionElement).not.toBeNull();
 | 
			
		||||
    expect(selectionElement.type).toEqual("selection");
 | 
			
		||||
    expect([selectionElement.x, selectionElement.y]).toEqual([60, 100]);
 | 
			
		||||
    expect([selectionElement.width, selectionElement.height]).toEqual([0, 0]);
 | 
			
		||||
 | 
			
		||||
    // TODO: There is a memory leak if mouse up is not triggered
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("resize selection element on mouse move", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("selection");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 60, clientY: 100 });
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 150, clientY: 30 });
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(2);
 | 
			
		||||
    const selectionElement = renderScene.mock.calls[1][1]!;
 | 
			
		||||
    expect(selectionElement).not.toBeNull();
 | 
			
		||||
    expect(selectionElement.type).toEqual("selection");
 | 
			
		||||
    expect([selectionElement.x, selectionElement.y]).toEqual([60, 30]);
 | 
			
		||||
    expect([selectionElement.width, selectionElement.height]).toEqual([90, 70]);
 | 
			
		||||
 | 
			
		||||
    // TODO: There is a memory leak if mouse up is not triggered
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("remove selection element on mouse up", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    // select tool
 | 
			
		||||
    const tool = getByToolName("selection");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 60, clientY: 100 });
 | 
			
		||||
    fireEvent.mouseMove(canvas, { clientX: 150, clientY: 30 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(3);
 | 
			
		||||
    const selectionElement = renderScene.mock.calls[2][1];
 | 
			
		||||
    expect(selectionElement).toBeNull();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("select single element on the scene", () => {
 | 
			
		||||
  it("rectangle", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    {
 | 
			
		||||
      // create element
 | 
			
		||||
      const tool = getByToolName("rectangle");
 | 
			
		||||
      fireEvent.click(tool);
 | 
			
		||||
      fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
      fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
      fireEvent.mouseUp(canvas);
 | 
			
		||||
      fireEvent.keyDown(document, { key: KEYS.ESCAPE });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tool = getByToolName("selection");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
    // click on a line on the rectangle
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 45, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(7);
 | 
			
		||||
    const elements = renderScene.mock.calls[6][0];
 | 
			
		||||
    const selectionElement = renderScene.mock.calls[6][1];
 | 
			
		||||
    expect(selectionElement).toBeNull();
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect(elements[0].isSelected).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("diamond", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    {
 | 
			
		||||
      // create element
 | 
			
		||||
      const tool = getByToolName("diamond");
 | 
			
		||||
      fireEvent.click(tool);
 | 
			
		||||
      fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
      fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
      fireEvent.mouseUp(canvas);
 | 
			
		||||
      fireEvent.keyDown(document, { key: KEYS.ESCAPE });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tool = getByToolName("selection");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
    // click on a line on the rectangle
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 45, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(7);
 | 
			
		||||
    const elements = renderScene.mock.calls[6][0];
 | 
			
		||||
    const selectionElement = renderScene.mock.calls[6][1];
 | 
			
		||||
    expect(selectionElement).toBeNull();
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect(elements[0].isSelected).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("ellipse", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    {
 | 
			
		||||
      // create element
 | 
			
		||||
      const tool = getByToolName("ellipse");
 | 
			
		||||
      fireEvent.click(tool);
 | 
			
		||||
      fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
      fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
      fireEvent.mouseUp(canvas);
 | 
			
		||||
      fireEvent.keyDown(document, { key: KEYS.ESCAPE });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tool = getByToolName("selection");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
    // click on a line on the rectangle
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 45, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(7);
 | 
			
		||||
    const elements = renderScene.mock.calls[6][0];
 | 
			
		||||
    const selectionElement = renderScene.mock.calls[6][1];
 | 
			
		||||
    expect(selectionElement).toBeNull();
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect(elements[0].isSelected).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("arrow", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    {
 | 
			
		||||
      // create element
 | 
			
		||||
      const tool = getByToolName("arrow");
 | 
			
		||||
      fireEvent.click(tool);
 | 
			
		||||
      fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
      fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
      fireEvent.mouseUp(canvas);
 | 
			
		||||
      fireEvent.keyDown(document, { key: KEYS.ESCAPE });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tool = getByToolName("selection");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
    // click on a line on the rectangle
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 45, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(7);
 | 
			
		||||
    const elements = renderScene.mock.calls[6][0];
 | 
			
		||||
    const selectionElement = renderScene.mock.calls[6][1];
 | 
			
		||||
    expect(selectionElement).toBeNull();
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect(elements[0].isSelected).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("arrow", () => {
 | 
			
		||||
    const { getByToolName, container } = render(<App />);
 | 
			
		||||
    const canvas = container.querySelector("canvas")!;
 | 
			
		||||
    {
 | 
			
		||||
      // create element
 | 
			
		||||
      const tool = getByToolName("line");
 | 
			
		||||
      fireEvent.click(tool);
 | 
			
		||||
      fireEvent.mouseDown(canvas, { clientX: 30, clientY: 20 });
 | 
			
		||||
      fireEvent.mouseMove(canvas, { clientX: 60, clientY: 70 });
 | 
			
		||||
      fireEvent.mouseUp(canvas);
 | 
			
		||||
      fireEvent.keyDown(document, { key: KEYS.ESCAPE });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tool = getByToolName("selection");
 | 
			
		||||
    fireEvent.click(tool);
 | 
			
		||||
    // click on a line on the rectangle
 | 
			
		||||
    fireEvent.mouseDown(canvas, { clientX: 45, clientY: 20 });
 | 
			
		||||
    fireEvent.mouseUp(canvas);
 | 
			
		||||
 | 
			
		||||
    expect(renderScene).toHaveBeenCalledTimes(7);
 | 
			
		||||
    const elements = renderScene.mock.calls[6][0];
 | 
			
		||||
    const selectionElement = renderScene.mock.calls[6][1];
 | 
			
		||||
    expect(selectionElement).toBeNull();
 | 
			
		||||
    expect(elements.length).toEqual(1);
 | 
			
		||||
    expect(elements[0].isSelected).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										30
									
								
								src/tests/test-utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/tests/test-utils.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
import {
 | 
			
		||||
  render,
 | 
			
		||||
  queries,
 | 
			
		||||
  RenderResult,
 | 
			
		||||
  RenderOptions,
 | 
			
		||||
} from "@testing-library/react";
 | 
			
		||||
 | 
			
		||||
import * as toolQueries from "./queries/toolQueries";
 | 
			
		||||
 | 
			
		||||
const customQueries = {
 | 
			
		||||
  ...queries,
 | 
			
		||||
  ...toolQueries,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type TestRenderFn = (
 | 
			
		||||
  ui: React.ReactElement,
 | 
			
		||||
  options?: Omit<RenderOptions, "queries">,
 | 
			
		||||
) => RenderResult<typeof customQueries>;
 | 
			
		||||
 | 
			
		||||
const renderApp: TestRenderFn = (ui, options) =>
 | 
			
		||||
  render(ui, {
 | 
			
		||||
    queries: customQueries,
 | 
			
		||||
    ...options,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
// re-export everything
 | 
			
		||||
export * from "@testing-library/react";
 | 
			
		||||
 | 
			
		||||
// override render method
 | 
			
		||||
export { renderApp as render };
 | 
			
		||||
		Reference in New Issue
	
	Block a user