From 35d588c38f46dc6ae70c0be68e3ad29c4daffdb8 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Mon, 18 Nov 2024 19:30:28 +0100 Subject: [PATCH 01/26] Install melange-testing-library --- dune-project | 6 ++ package-lock.json | 173 ++++++++++++++++++++++++++++++++++++---------- reason-react.opam | 1 + test/dune | 8 ++- 4 files changed, 152 insertions(+), 36 deletions(-) diff --git a/dune-project b/dune-project index fbeb92d80..427e00e73 100644 --- a/dune-project +++ b/dune-project @@ -41,6 +41,12 @@ (reason (>= 3.12.0)) (ocaml-lsp-server :with-dev-setup) + (melange-testing-library + (and + (= "0.1.0") + :with-test + :with-dev-setup + :post)) (opam-check-npm-deps (and (= 1.0.0) diff --git a/package-lock.json b/package-lock.json index 56484d8a6..b11818400 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,10 @@ "name": "reason-react", "version": "0.11.0", "license": "MIT", + "dependencies": { + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.0.1" + }, "devDependencies": { "jest": "^26.0.1", "react": "^18.2.0", @@ -32,7 +36,6 @@ "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, "dependencies": { "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" @@ -45,7 +48,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -57,7 +59,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -71,7 +72,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -79,14 +79,12 @@ "node_modules/@babel/code-frame/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -95,7 +93,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -104,7 +101,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -293,7 +289,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -325,7 +320,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -339,7 +333,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -351,7 +344,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -365,7 +357,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -373,14 +364,12 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -389,7 +378,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -398,7 +386,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -565,6 +552,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -954,6 +953,78 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/react": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", + "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -963,6 +1034,12 @@ "node": ">= 6" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.2", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", @@ -1156,7 +1233,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -1165,7 +1241,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1198,6 +1273,15 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -1554,7 +1638,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1729,7 +1812,6 @@ "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, "dependencies": { "color-name": "~1.1.4" }, @@ -1740,8 +1822,7 @@ "node_modules/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 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/combined-stream": { "version": "1.0.8", @@ -1906,6 +1987,15 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -1924,6 +2014,12 @@ "node": ">= 10.14.2" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "license": "MIT" + }, "node_modules/domexception": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", @@ -2524,7 +2620,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -3565,8 +3660,7 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "3.14.1", @@ -3712,7 +3806,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -3729,6 +3822,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -4417,7 +4519,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -4429,7 +4530,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -4441,8 +4541,7 @@ "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/react-shallow-renderer": { "version": "16.15.0", @@ -4527,6 +4626,12 @@ "node": ">=8" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, "node_modules/regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -4988,7 +5093,6 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -5558,7 +5662,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, diff --git a/reason-react.opam b/reason-react.opam index e2329051c..13fb0a5f3 100644 --- a/reason-react.opam +++ b/reason-react.opam @@ -23,6 +23,7 @@ depends: [ "reason-react-ppx" {= version} "reason" {>= "3.12.0"} "ocaml-lsp-server" {with-dev-setup} + "melange-testing-library" {= "0.1.0" & with-test & with-dev-setup & post} "opam-check-npm-deps" {= "1.0.0" & with-dev-setup} "ocamlformat" {= "0.24.0" & with-dev-setup} "odoc" {with-doc} diff --git a/test/dune b/test/dune index a50a84e20..fd66b7258 100644 --- a/test/dune +++ b/test/dune @@ -3,6 +3,12 @@ (target test) (module_systems (commonjs bs.js)) - (libraries reason-react reason-react.node jest melange.belt) + (libraries + reason-react + reason-react.node + jest + melange.belt + melange-testing-library.dom + melange-testing-library.react) (preprocess (pps melange.ppx reason-react-ppx))) From 1cd6a6b919398123511ee13a78aba2764c91b047 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Mon, 18 Nov 2024 19:31:51 +0100 Subject: [PATCH 02/26] Install melange-testing-library npm deps --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 63bf49935..6020ed48a 100644 --- a/package.json +++ b/package.json @@ -40,5 +40,9 @@ "testMatch": [ "**/*__test.bs.js" ] + }, + "dependencies": { + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.0.1" } } From 05aa7e129bd028f701e93530f00ea4a3e8a175e4 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Mon, 18 Nov 2024 20:52:39 +0100 Subject: [PATCH 03/26] Vendor melange-testing-library --- test/melange-testing-library/README.md | 5 + .../dom/DomTestingLibrary.re | 4 + .../dom/DomTestingLibrary.rei | 858 +++++++++++++ test/melange-testing-library/dom/FireEvent.re | 404 ++++++ .../melange-testing-library/dom/FireEvent.rei | 71 ++ test/melange-testing-library/dom/Queries.re | 1126 +++++++++++++++++ test/melange-testing-library/dom/Utils.re | 176 +++ test/melange-testing-library/dom/dune | 7 + .../react/ReactTestingLibrary.re | 1044 +++++++++++++++ .../react/ReactTestingLibrary.rei | 702 ++++++++++ test/melange-testing-library/react/dune | 7 + 11 files changed, 4404 insertions(+) create mode 100644 test/melange-testing-library/README.md create mode 100644 test/melange-testing-library/dom/DomTestingLibrary.re create mode 100644 test/melange-testing-library/dom/DomTestingLibrary.rei create mode 100644 test/melange-testing-library/dom/FireEvent.re create mode 100644 test/melange-testing-library/dom/FireEvent.rei create mode 100644 test/melange-testing-library/dom/Queries.re create mode 100644 test/melange-testing-library/dom/Utils.re create mode 100644 test/melange-testing-library/dom/dune create mode 100644 test/melange-testing-library/react/ReactTestingLibrary.re create mode 100644 test/melange-testing-library/react/ReactTestingLibrary.rei create mode 100644 test/melange-testing-library/react/dune diff --git a/test/melange-testing-library/README.md b/test/melange-testing-library/README.md new file mode 100644 index 000000000..9711773d1 --- /dev/null +++ b/test/melange-testing-library/README.md @@ -0,0 +1,5 @@ +### vendored melange-testing-library + +This directory contains a vendored version of the melange-testing-library. + +The original repository is https://github.com/melange-community/melange-testing-library diff --git a/test/melange-testing-library/dom/DomTestingLibrary.re b/test/melange-testing-library/dom/DomTestingLibrary.re new file mode 100644 index 000000000..b2845661b --- /dev/null +++ b/test/melange-testing-library/dom/DomTestingLibrary.re @@ -0,0 +1,4 @@ +include Queries; +include Utils; + +module FireEvent = FireEvent; diff --git a/test/melange-testing-library/dom/DomTestingLibrary.rei b/test/melange-testing-library/dom/DomTestingLibrary.rei new file mode 100644 index 000000000..f8989f68b --- /dev/null +++ b/test/melange-testing-library/dom/DomTestingLibrary.rei @@ -0,0 +1,858 @@ +module FireEvent = FireEvent; + +module ByLabelTextQuery: { + type options = { + . + "selector": Js.undefined(string), + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + [@mel.obj] + external makeOptions: + ( + ~selector: string=?, + ~exact: bool=?, + ~normalizer: string => string=?, + unit + ) => + options; +}; +module ByPlaceholderTextQuery: { + type options = { + . + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + [@mel.obj] + external makeOptions: + (~exact: bool=?, ~normalizer: string => string=?, unit) => options; +}; +module ByTextQuery: { + type options = { + . + "exact": Js.undefined(bool), + "selector": Js.undefined(string), + "ignore": Js.undefined(string), + "normalizer": Js.undefined(string => string), + }; + [@mel.obj] + external makeOptions: + ( + ~exact: bool=?, + ~selector: string=?, + ~ignore: string=?, + ~normalizer: string => string=?, + unit + ) => + options; +}; +module ByAltTextQuery: { + type options = { + . + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + [@mel.obj] + external makeOptions: + (~exact: bool=?, ~normalizer: string => string=?, unit) => options; +}; +module ByTitleQuery: { + type options = { + . + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + [@mel.obj] + external makeOptions: + (~exact: bool=?, ~normalizer: string => string=?, unit) => options; +}; +module ByDisplayValueQuery: { + type options = { + . + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + [@mel.obj] + external makeOptions: + (~exact: bool=?, ~normalizer: string => string=?, unit) => options; +}; +module ByRoleQuery: { + type options = { + . + "checked": Js.undefined(bool), + "collapseWhitespace": Js.undefined(bool), + "exact": Js.undefined(bool), + "hidden": Js.undefined(bool), + "level": Js.undefined(int), + "pressed": Js.undefined(bool), + "name": Js.undefined(string), + "normalizer": Js.undefined(string => string), + "queryFallbacks": Js.undefined(bool), + "selected": Js.undefined(bool), + "trim": Js.undefined(bool), + }; + [@mel.obj] + external makeOptions: + ( + ~checked: bool=?, + ~collapseWhitespace: bool=?, + ~exact: bool=?, + ~hidden: bool=?, + ~level: int=?, + ~pressed: bool=?, + ~name: string=?, + ~normalizer: string => string=?, + ~queryFallbacks: bool=?, + ~selected: bool=?, + ~trim: bool=?, + unit + ) => + options; +}; +module ByTestIdQuery: { + type options = { + . + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + [@mel.obj] + external makeOptions: + (~exact: bool=?, ~normalizer: string => string=?, unit) => options; +}; +module MutationObserver: { + type options = { + . + "attributeFilter": Js.undefined(array(string)), + "attributeOldValue": Js.undefined(bool), + "attributes": Js.undefined(bool), + "characterData": Js.undefined(bool), + "characterDataOldValue": Js.undefined(bool), + "childList": Js.undefined(bool), + "subtree": Js.undefined(bool), + }; + [@mel.obj] + external makeOptions: + ( + ~attributeFilter: array(string)=?, + ~attributeOldValue: bool=?, + ~attributes: bool=?, + ~characterData: bool=?, + ~characterDataOldValue: bool=?, + ~childList: bool=?, + ~subtree: bool=?, + unit + ) => + options; +}; +module WaitFor: { + type options = { + . + "container": Js.undefined(Dom.element), + "interval": Js.undefined(int), + "mutationObserverOptions": Js.undefined(MutationObserver.options), + "onTimeout": Js.undefined(Js.Exn.t => Js.Exn.t), + "showOriginalStackTrace": Js.undefined(bool), + "stackTraceError": Js.undefined(Js.Exn.t), + "timeout": Js.undefined(int), + }; + [@mel.obj] + external makeOptions: + ( + ~container: Dom.element=?, + ~interval: int=?, + ~mutationObserverOptions: MutationObserver.options=?, + ~onTimeout: Js.Exn.t => Js.Exn.t=?, + ~showOriginalStackTrace: bool=?, + ~stackTraceError: Js.Exn.t=?, + ~timeout: int=?, + unit + ) => + options; +}; +module WaitForElement: { + type options = { + . + "container": Js.undefined(Dom.element), + "timeout": Js.undefined(int), + }; + [@mel.obj] + external makeOptions: + ( + ~container: Dom.element=?, + ~mutationObserverInit: MutationObserver.options=?, + ~timeout: int=?, + unit + ) => + options; +}; + +let waitFor: + (~callback: unit => unit, ~options: WaitFor.options=?, unit) => + Js.Promise.t('a); + +let waitForPromise: + (~callback: unit => Js.Promise.t('a), ~options: WaitFor.options=?, unit) => + Js.Promise.t('b); + +let waitForElement: + (~callback: unit => 'a=?, ~options: WaitForElement.options=?, unit) => + Js.Promise.t('a); + +let waitForElementToBeRemoved: + ( + ~callback: [ + | `Func(unit => 'a) + | `Value('a) + ], + ~options: WaitFor.options=?, + unit + ) => + Js.Promise.t(unit); + +let prettyDOM: (~maxLength: int=?, Dom.element) => string; + +let logDOM: (~maxLength: int=?, Dom.element) => unit; + +module Configure: { + type options = { + . + "_disableExpensiveErrorDiagnostics": Js.undefined(bool), + "asyncUtilTimeout": Js.undefined(int), + "asyncWrapper": Js.undefined(unit => unit), + "computedStyleSupportsPseudoElements": Js.undefined(bool), + "defaultHidden": Js.undefined(bool), + "eventWrapper": Js.undefined(unit => unit), + "getElementError": Js.undefined((string, Dom.element) => Js.Exn.t), + "showOriginalStackTrace": Js.undefined(bool), + "testIdAttribute": Js.undefined(string), + "throwSuggestions": Js.undefined(bool), + }; + [@mel.obj] + external makeOptions: + ( + ~_disableExpensiveErrorDiagnostics: bool=?, + ~asyncUtilTimeout: int=?, + ~asyncWrapper: unit => unit=?, + ~computedStyleSupportsPseudoElements: bool=?, + ~defaultHidden: bool=?, + ~eventWrapper: unit => unit=?, + ~getElementError: (string, Dom.element) => Js.Exn.t=?, + ~showOriginalStackTrace: bool=?, + ~testIdAttribute: string=?, + ~throwSuggestions: bool=?, + unit + ) => + options; +}; + +let configure: + ( + ~update: [ + | `Func(Configure.options => Configure.options) + | `Object(Configure.options) + ] + ) => + unit; + +[@mel.module "@testing-library/dom"] +external getNodeText: Dom.element => string = "getNodeText"; + +/** + * ByLabelText + */ +let getByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByLabelTextQuery.options=?, + Dom.element + ) => + Dom.element; + +let getAllByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByLabelTextQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let queryByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByLabelTextQuery.options=?, + Dom.element + ) => + Js.null(Dom.element); + +let queryAllByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByLabelTextQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let findByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByLabelTextQuery.options=?, + Dom.element + ) => + Js.Promise.t(Dom.element); + +let findAllByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByLabelTextQuery.options=?, + Dom.element + ) => + Js.Promise.t(array(Dom.element)); + +/** + * ByPlaceholderText + */ +let getByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByPlaceholderTextQuery.options=?, + Dom.element + ) => + Dom.element; + +let getAllByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByPlaceholderTextQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let queryByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByPlaceholderTextQuery.options=?, + Dom.element + ) => + Js.null(Dom.element); + +let queryAllByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByPlaceholderTextQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let findByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByPlaceholderTextQuery.options=?, + Dom.element + ) => + Js.Promise.t(Dom.element); + +let findAllByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByPlaceholderTextQuery.options=?, + Dom.element + ) => + Js.Promise.t(array(Dom.element)); + +/** + * ByText + */ +let getByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTextQuery.options=?, + Dom.element + ) => + Dom.element; + +let getAllByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTextQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let queryByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTextQuery.options=?, + Dom.element + ) => + Js.null(Dom.element); + +let queryAllByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTextQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let findByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTextQuery.options=?, + Dom.element + ) => + Js.Promise.t(Dom.element); + +let findAllByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTextQuery.options=?, + Dom.element + ) => + Js.Promise.t(array(Dom.element)); + +/** + * ByAltText + */ +let getByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByAltTextQuery.options=?, + Dom.element + ) => + Dom.element; + +let getAllByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByAltTextQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let queryByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByAltTextQuery.options=?, + Dom.element + ) => + Js.null(Dom.element); + +let queryAllByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByAltTextQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let findByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByAltTextQuery.options=?, + Dom.element + ) => + Js.Promise.t(Dom.element); + +let findAllByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByAltTextQuery.options=?, + Dom.element + ) => + Js.Promise.t(array(Dom.element)); + +/** + * ByTitle + */ +let getByTitle: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTitleQuery.options=?, + Dom.element + ) => + Dom.element; + +let getAllByTitle: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTitleQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let queryByTitle: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTitleQuery.options=?, + Dom.element + ) => + Js.null(Dom.element); + +let queryAllByTitle: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTitleQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let findByTitle: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTitleQuery.options=?, + Dom.element + ) => + Js.Promise.t(Dom.element); + +let findAllByTitle: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTitleQuery.options=?, + Dom.element + ) => + Js.Promise.t(array(Dom.element)); + +/** + * ByDisplayValue + */ +let getByDisplayValue: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByDisplayValueQuery.options=?, + Dom.element + ) => + Dom.element; + +let getAllByDisplayValue: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByDisplayValueQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let queryByDisplayValue: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByDisplayValueQuery.options=?, + Dom.element + ) => + Js.null(Dom.element); + +let queryAllByDisplayValue: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByDisplayValueQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let findByDisplayValue: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByDisplayValueQuery.options=?, + Dom.element + ) => + Js.Promise.t(Dom.element); + +let findAllByDisplayValue: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByDisplayValueQuery.options=?, + Dom.element + ) => + Js.Promise.t(array(Dom.element)); + +/** + * ByRole + */ +let getByRole: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByRoleQuery.options=?, + Dom.element + ) => + Dom.element; + +let getAllByRole: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByRoleQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let queryByRole: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByRoleQuery.options=?, + Dom.element + ) => + Js.null(Dom.element); + +let queryAllByRole: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByRoleQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let findByRole: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByRoleQuery.options=?, + Dom.element + ) => + Js.Promise.t(Dom.element); + +let findAllByRole: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByRoleQuery.options=?, + Dom.element + ) => + Js.Promise.t(array(Dom.element)); + +/** + * ByTestId + */ +let getByTestId: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTestIdQuery.options=?, + Dom.element + ) => + Dom.element; + +let getAllByTestId: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTestIdQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let queryByTestId: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTestIdQuery.options=?, + Dom.element + ) => + Js.null(Dom.element); + +let queryAllByTestId: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTestIdQuery.options=?, + Dom.element + ) => + array(Dom.element); + +let findByTestId: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTestIdQuery.options=?, + Dom.element + ) => + Js.Promise.t(Dom.element); + +let findAllByTestId: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: ByTestIdQuery.options=?, + Dom.element + ) => + Js.Promise.t(array(Dom.element)); diff --git a/test/melange-testing-library/dom/FireEvent.re b/test/melange-testing-library/dom/FireEvent.re new file mode 100644 index 000000000..0fb3b560b --- /dev/null +++ b/test/melange-testing-library/dom/FireEvent.re @@ -0,0 +1,404 @@ +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _abort: (Dom.element, Js.undefined(Js.t({..}))) => unit = "abort"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _animationEnd: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "animationEnd"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _animationIteration: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "animationIteration"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _animationStart: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "animationStart"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _blur: (Dom.element, Js.undefined(Js.t({..}))) => unit = "blur"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _canPlay: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "canPlay"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _canPlayThrough: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "canPlayThrough"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _change: (Dom.element, Js.undefined(Js.t({..}))) => unit = "change"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _click: (Dom.element, Js.undefined(Js.t({..}))) => unit = "click"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _compositionEnd: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "compositionEnd"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _compositionStart: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "compositionStart"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _compositionUpdate: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "compositionUpdate"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _contextMenu: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "contextMenu"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _copy: (Dom.element, Js.undefined(Js.t({..}))) => unit = "copy"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _cut: (Dom.element, Js.undefined(Js.t({..}))) => unit = "cut"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _dblClick: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "dblClick"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _doubleClick: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "doubleClick"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _drag: (Dom.element, Js.undefined(Js.t({..}))) => unit = "drag"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _dragEnd: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "dragEnd"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _dragEnter: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "dragEnter"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _dragExit: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "dragExit"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _dragLeave: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "dragLeave"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _dragOver: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "dragOver"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _dragStart: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "dragStart"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _drop: (Dom.element, Js.undefined(Js.t({..}))) => unit = "drop"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _durationChange: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "durationChange"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _emptied: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "emptied"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _encrypted: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "encrypted"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _ended: (Dom.element, Js.undefined(Js.t({..}))) => unit = "ended"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _error: (Dom.element, Js.undefined(Js.t({..}))) => unit = "error"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _focus: (Dom.element, Js.undefined(Js.t({..}))) => unit = "focus"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _focusIn: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "focusIn"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _focusOut: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "focusOut"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _input: (Dom.element, Js.undefined(Js.t({..}))) => unit = "input"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _invalid: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "invalid"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _keyDown: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "keyDown"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _keyPress: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "keyPress"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _keyUp: (Dom.element, Js.undefined(Js.t({..}))) => unit = "keyUp"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _load: (Dom.element, Js.undefined(Js.t({..}))) => unit = "load"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _loadStart: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "loadStart"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _loadedData: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "loadedData"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _loadedMetadata: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "loadedMetadata"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _mouseDown: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "mouseDown"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _mouseEnter: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "mouseEnter"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _mouseLeave: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "mouseLeave"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _mouseMove: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "mouseMove"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _mouseOut: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "mouseOut"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _mouseOver: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "mouseOver"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _mouseUp: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "mouseUp"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _paste: (Dom.element, Js.undefined(Js.t({..}))) => unit = "paste"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _pause: (Dom.element, Js.undefined(Js.t({..}))) => unit = "pause"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _play: (Dom.element, Js.undefined(Js.t({..}))) => unit = "play"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _playing: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "playing"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _progress: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "progress"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _rateChange: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "rateChange"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _scroll: (Dom.element, Js.undefined(Js.t({..}))) => unit = "scroll"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _seeked: (Dom.element, Js.undefined(Js.t({..}))) => unit = "seeked"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _seeking: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "seeking"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _select: (Dom.element, Js.undefined(Js.t({..}))) => unit = "select"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _stalled: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "stalled"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _submit: (Dom.element, Js.undefined(Js.t({..}))) => unit = "submit"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _suspend: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "suspend"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _timeUpdate: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "timeUpdate"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _touchCancel: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "touchCancel"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _touchEnd: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "touchEnd"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _touchMove: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "touchMove"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _touchStart: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "touchStart"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _transitionEnd: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "transitionEnd"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _volumeChange: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "volumeChange"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _waiting: (Dom.element, Js.undefined(Js.t({..}))) => unit = + "waiting"; + +[@mel.module "@testing-library/dom"] [@mel.scope "fireEvent"] +external _wheel: (Dom.element, Js.undefined(Js.t({..}))) => unit = "wheel"; + +let abort = (~eventInit=?, el) => + _abort(el, Js.Undefined.fromOption(eventInit)); +let animationEnd = (~eventInit=?, el) => + _animationEnd(el, Js.Undefined.fromOption(eventInit)); +let animationIteration = (~eventInit=?, el) => + _animationIteration(el, Js.Undefined.fromOption(eventInit)); +let animationStart = (~eventInit=?, el) => + _animationStart(el, Js.Undefined.fromOption(eventInit)); +let blur = (~eventInit=?, el) => + _blur(el, Js.Undefined.fromOption(eventInit)); +let canPlay = (~eventInit=?, el) => + _canPlay(el, Js.Undefined.fromOption(eventInit)); +let canPlayThrough = (~eventInit=?, el) => + _canPlayThrough(el, Js.Undefined.fromOption(eventInit)); +let change = (~eventInit=?, el) => + _change(el, Js.Undefined.fromOption(eventInit)); +let click = (~eventInit=?, el) => + _click(el, Js.Undefined.fromOption(eventInit)); +let compositionEnd = (~eventInit=?, el) => + _compositionEnd(el, Js.Undefined.fromOption(eventInit)); +let compositionStart = (~eventInit=?, el) => + _compositionStart(el, Js.Undefined.fromOption(eventInit)); +let compositionUpdate = (~eventInit=?, el) => + _compositionUpdate(el, Js.Undefined.fromOption(eventInit)); +let contextMenu = (~eventInit=?, el) => + _contextMenu(el, Js.Undefined.fromOption(eventInit)); +let copy = (~eventInit=?, el) => + _copy(el, Js.Undefined.fromOption(eventInit)); +let cut = (~eventInit=?, el) => + _cut(el, Js.Undefined.fromOption(eventInit)); +let dblClick = (~eventInit=?, el) => + _dblClick(el, Js.Undefined.fromOption(eventInit)); +let doubleClick = (~eventInit=?, el) => + _doubleClick(el, Js.Undefined.fromOption(eventInit)); +let drag = (~eventInit=?, el) => + _drag(el, Js.Undefined.fromOption(eventInit)); +let dragEnd = (~eventInit=?, el) => + _dragEnd(el, Js.Undefined.fromOption(eventInit)); +let dragEnter = (~eventInit=?, el) => + _dragEnter(el, Js.Undefined.fromOption(eventInit)); +let dragExit = (~eventInit=?, el) => + _dragExit(el, Js.Undefined.fromOption(eventInit)); +let dragLeave = (~eventInit=?, el) => + _dragLeave(el, Js.Undefined.fromOption(eventInit)); +let dragOver = (~eventInit=?, el) => + _dragOver(el, Js.Undefined.fromOption(eventInit)); +let dragStart = (~eventInit=?, el) => + _dragStart(el, Js.Undefined.fromOption(eventInit)); +let drop = (~eventInit=?, el) => + _drop(el, Js.Undefined.fromOption(eventInit)); +let durationChange = (~eventInit=?, el) => + _durationChange(el, Js.Undefined.fromOption(eventInit)); +let emptied = (~eventInit=?, el) => + _emptied(el, Js.Undefined.fromOption(eventInit)); +let encrypted = (~eventInit=?, el) => + _encrypted(el, Js.Undefined.fromOption(eventInit)); +let ended = (~eventInit=?, el) => + _ended(el, Js.Undefined.fromOption(eventInit)); +let error = (~eventInit=?, el) => + _error(el, Js.Undefined.fromOption(eventInit)); +let focus = (~eventInit=?, el) => + _focus(el, Js.Undefined.fromOption(eventInit)); +let focusIn = (~eventInit=?, el) => + _focusIn(el, Js.Undefined.fromOption(eventInit)); +let focusOut = (~eventInit=?, el) => + _focusOut(el, Js.Undefined.fromOption(eventInit)); +let input = (~eventInit=?, el) => + _input(el, Js.Undefined.fromOption(eventInit)); +let invalid = (~eventInit=?, el) => + _invalid(el, Js.Undefined.fromOption(eventInit)); +let keyDown = (~eventInit=?, el) => + _keyDown(el, Js.Undefined.fromOption(eventInit)); +let keyPress = (~eventInit=?, el) => + _keyPress(el, Js.Undefined.fromOption(eventInit)); +let keyUp = (~eventInit=?, el) => + _keyUp(el, Js.Undefined.fromOption(eventInit)); +let load = (~eventInit=?, el) => + _load(el, Js.Undefined.fromOption(eventInit)); +let loadStart = (~eventInit=?, el) => + _loadStart(el, Js.Undefined.fromOption(eventInit)); +let loadedData = (~eventInit=?, el) => + _loadedData(el, Js.Undefined.fromOption(eventInit)); +let loadedMetadata = (~eventInit=?, el) => + _loadedMetadata(el, Js.Undefined.fromOption(eventInit)); +let mouseDown = (~eventInit=?, el) => + _mouseDown(el, Js.Undefined.fromOption(eventInit)); +let mouseEnter = (~eventInit=?, el) => + _mouseEnter(el, Js.Undefined.fromOption(eventInit)); +let mouseLeave = (~eventInit=?, el) => + _mouseLeave(el, Js.Undefined.fromOption(eventInit)); +let mouseMove = (~eventInit=?, el) => + _mouseMove(el, Js.Undefined.fromOption(eventInit)); +let mouseOut = (~eventInit=?, el) => + _mouseOut(el, Js.Undefined.fromOption(eventInit)); +let mouseOver = (~eventInit=?, el) => + _mouseOver(el, Js.Undefined.fromOption(eventInit)); +let mouseUp = (~eventInit=?, el) => + _mouseUp(el, Js.Undefined.fromOption(eventInit)); +let paste = (~eventInit=?, el) => + _paste(el, Js.Undefined.fromOption(eventInit)); +let pause = (~eventInit=?, el) => + _pause(el, Js.Undefined.fromOption(eventInit)); +let play = (~eventInit=?, el) => + _play(el, Js.Undefined.fromOption(eventInit)); +let playing = (~eventInit=?, el) => + _playing(el, Js.Undefined.fromOption(eventInit)); +let progress = (~eventInit=?, el) => + _progress(el, Js.Undefined.fromOption(eventInit)); +let rateChange = (~eventInit=?, el) => + _rateChange(el, Js.Undefined.fromOption(eventInit)); +let scroll = (~eventInit=?, el) => + _scroll(el, Js.Undefined.fromOption(eventInit)); +let seeked = (~eventInit=?, el) => + _seeked(el, Js.Undefined.fromOption(eventInit)); +let seeking = (~eventInit=?, el) => + _seeking(el, Js.Undefined.fromOption(eventInit)); +let select = (~eventInit=?, el) => + _select(el, Js.Undefined.fromOption(eventInit)); +let stalled = (~eventInit=?, el) => + _stalled(el, Js.Undefined.fromOption(eventInit)); +let submit = (~eventInit=?, el) => + _submit(el, Js.Undefined.fromOption(eventInit)); +let suspend = (~eventInit=?, el) => + _suspend(el, Js.Undefined.fromOption(eventInit)); +let timeUpdate = (~eventInit=?, el) => + _timeUpdate(el, Js.Undefined.fromOption(eventInit)); +let touchCancel = (~eventInit=?, el) => + _touchCancel(el, Js.Undefined.fromOption(eventInit)); +let touchEnd = (~eventInit=?, el) => + _touchEnd(el, Js.Undefined.fromOption(eventInit)); +let touchMove = (~eventInit=?, el) => + _touchMove(el, Js.Undefined.fromOption(eventInit)); +let touchStart = (~eventInit=?, el) => + _touchStart(el, Js.Undefined.fromOption(eventInit)); +let transitionEnd = (~eventInit=?, el) => + _transitionEnd(el, Js.Undefined.fromOption(eventInit)); +let volumeChange = (~eventInit=?, el) => + _volumeChange(el, Js.Undefined.fromOption(eventInit)); +let waiting = (~eventInit=?, el) => + _waiting(el, Js.Undefined.fromOption(eventInit)); +let wheel = (~eventInit=?, el) => + _wheel(el, Js.Undefined.fromOption(eventInit)); diff --git a/test/melange-testing-library/dom/FireEvent.rei b/test/melange-testing-library/dom/FireEvent.rei new file mode 100644 index 000000000..1c791995b --- /dev/null +++ b/test/melange-testing-library/dom/FireEvent.rei @@ -0,0 +1,71 @@ +let abort: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let animationEnd: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let animationIteration: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let animationStart: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let blur: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let canPlay: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let canPlayThrough: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let change: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let click: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let compositionEnd: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let compositionStart: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let compositionUpdate: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let contextMenu: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let copy: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let cut: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let dblClick: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let doubleClick: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let drag: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let dragEnd: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let dragEnter: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let dragExit: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let dragLeave: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let dragOver: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let dragStart: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let drop: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let durationChange: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let emptied: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let encrypted: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let ended: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let error: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let focus: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let focusIn: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let focusOut: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let input: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let invalid: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let keyDown: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let keyPress: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let keyUp: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let load: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let loadStart: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let loadedData: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let loadedMetadata: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let mouseDown: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let mouseEnter: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let mouseLeave: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let mouseMove: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let mouseOut: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let mouseOver: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let mouseUp: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let paste: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let pause: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let play: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let playing: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let progress: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let rateChange: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let scroll: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let seeked: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let seeking: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let select: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let stalled: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let submit: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let suspend: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let timeUpdate: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let touchCancel: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let touchEnd: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let touchMove: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let touchStart: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let transitionEnd: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let volumeChange: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let waiting: (~eventInit: Js.t({..})=?, Dom.element) => unit; +let wheel: (~eventInit: Js.t({..})=?, Dom.element) => unit; diff --git a/test/melange-testing-library/dom/Queries.re b/test/melange-testing-library/dom/Queries.re new file mode 100644 index 000000000..0c05fd206 --- /dev/null +++ b/test/melange-testing-library/dom/Queries.re @@ -0,0 +1,1126 @@ +module ByLabelTextQuery = { + type options = { + . + "selector": Js.undefined(string), + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + + [@mel.obj] + external makeOptions: + ( + ~selector: string=?, + ~exact: bool=?, + ~normalizer: string => string=?, + unit + ) => + options; +}; +module ByPlaceholderTextQuery = { + type options = { + . + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + + [@mel.obj] + external makeOptions: + (~exact: bool=?, ~normalizer: string => string=?, unit) => options; +}; +module ByTextQuery = { + type options = { + . + "exact": Js.undefined(bool), + "selector": Js.undefined(string), + "ignore": Js.undefined(string), + "normalizer": Js.undefined(string => string), + }; + + [@mel.obj] + external makeOptions: + ( + ~exact: bool=?, + ~selector: string=?, + ~ignore: string=?, + ~normalizer: string => string=?, + unit + ) => + options; +}; +module ByAltTextQuery = { + type options = { + . + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + + [@mel.obj] + external makeOptions: + (~exact: bool=?, ~normalizer: string => string=?, unit) => options; +}; +module ByTitleQuery = { + type options = { + . + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + + [@mel.obj] + external makeOptions: + (~exact: bool=?, ~normalizer: string => string=?, unit) => options; +}; +module ByDisplayValueQuery = { + type options = { + . + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + + [@mel.obj] + external makeOptions: + (~exact: bool=?, ~normalizer: string => string=?, unit) => options; +}; +module ByRoleQuery = { + type options = { + . + "checked": Js.undefined(bool), + "collapseWhitespace": Js.undefined(bool), + "exact": Js.undefined(bool), + "hidden": Js.undefined(bool), + "level": Js.undefined(int), + "pressed": Js.undefined(bool), + "name": Js.undefined(string), + "normalizer": Js.undefined(string => string), + "queryFallbacks": Js.undefined(bool), + "selected": Js.undefined(bool), + "trim": Js.undefined(bool), + }; + + [@mel.obj] + external makeOptions: + ( + ~checked: bool=?, + ~collapseWhitespace: bool=?, + ~exact: bool=?, + ~hidden: bool=?, + ~level: int=?, + ~pressed: bool=?, + ~name: string=?, + ~normalizer: string => string=?, + ~queryFallbacks: bool=?, + ~selected: bool=?, + ~trim: bool=?, + unit + ) => + options; +}; +module ByTestIdQuery = { + type options = { + . + "exact": Js.undefined(bool), + "normalizer": Js.undefined(string => string), + }; + + [@mel.obj] + external makeOptions: + (~exact: bool=?, ~normalizer: string => string=?, unit) => options; +}; + +[@mel.module "@testing-library/dom"] +external getNodeText: Dom.element => string = "getNodeText"; + +/** + * ByLabelText + */ +[@mel.module "@testing-library/dom"] +external _getByLabelText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + Dom.element = + "getByLabelText"; + +let getByLabelText = (~matcher, ~options=?, element) => + _getByLabelText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _getAllByLabelText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + array(Dom.element) = + "getAllByLabelText"; + +let getAllByLabelText = (~matcher, ~options=?, element) => + _getAllByLabelText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryByLabelText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + Js.null(Dom.element) = + "queryByLabelText"; + +let queryByLabelText = (~matcher, ~options=?, element) => + _queryByLabelText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryAllByLabelText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + array(Dom.element) = + "queryAllByLabelText"; + +let queryAllByLabelText = (~matcher, ~options=?, element) => + _queryAllByLabelText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findByLabelText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByLabelText"; + +let findByLabelText = (~matcher, ~options=?, element) => + _findByLabelText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findAllByLabelText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByLabelText"; + +let findAllByLabelText = (~matcher, ~options=?, element) => + _findAllByLabelText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +/** + * ByPlaceholderText + */ +[@mel.module "@testing-library/dom"] +external _getByPlaceholderText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + Dom.element = + "getByPlaceholderText"; + +let getByPlaceholderText = (~matcher, ~options=?, element) => + _getByPlaceholderText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _getAllByPlaceholderText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + array(Dom.element) = + "getAllByPlaceholderText"; + +let getAllByPlaceholderText = (~matcher, ~options=?, element) => + _getAllByPlaceholderText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryByPlaceholderText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + Js.null(Dom.element) = + "queryByPlaceholderText"; + +let queryByPlaceholderText = (~matcher, ~options=?, element) => + _queryByPlaceholderText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryAllByPlaceholderText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + array(Dom.element) = + "queryAllByPlaceholderText"; + +let queryAllByPlaceholderText = (~matcher, ~options=?, element) => + _queryAllByPlaceholderText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findByPlaceholderText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByPlaceholderText"; + +let findByPlaceholderText = (~matcher, ~options=?, element) => + _findByPlaceholderText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findAllByPlaceholderText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByPlaceholderText"; + +let findAllByPlaceholderText = (~matcher, ~options=?, element) => + _findAllByPlaceholderText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +/** + * ByText + */ +[@mel.module "@testing-library/dom"] +external _getByText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + Dom.element = + "getByText"; + +let getByText = (~matcher, ~options=?, element) => + _getByText(element, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _getAllByText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + array(Dom.element) = + "getAllByText"; + +let getAllByText = (~matcher, ~options=?, element) => + _getAllByText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryByText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + Js.null(Dom.element) = + "queryByText"; + +let queryByText = (~matcher, ~options=?, element) => + _queryByText(element, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _queryAllByText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + array(Dom.element) = + "queryAllByText"; + +let queryAllByText = (~matcher, ~options=?, element) => + _queryAllByText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findByText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByText"; + +let findByText = (~matcher, ~options=?, element) => + _findByText(element, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _findAllByText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByText"; + +let findAllByText = (~matcher, ~options=?, element) => + _findAllByText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +/** + * ByAltText + */ +[@mel.module "@testing-library/dom"] +external _getByAltText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + Dom.element = + "getByAltText"; + +let getByAltText = (~matcher, ~options=?, element) => + _getByAltText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _getAllByAltText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + array(Dom.element) = + "getAllByAltText"; + +let getAllByAltText = (~matcher, ~options=?, element) => + _getAllByAltText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryByAltText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + Js.null(Dom.element) = + "queryByAltText"; + +let queryByAltText = (~matcher, ~options=?, element) => + _queryByAltText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryAllByAltText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + array(Dom.element) = + "queryAllByAltText"; + +let queryAllByAltText = (~matcher, ~options=?, element) => + _queryAllByAltText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findByAltText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByAltText"; + +let findByAltText = (~matcher, ~options=?, element) => + _findByAltText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findAllByAltText: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByAltText"; + +let findAllByAltText = (~matcher, ~options=?, element) => + _findAllByAltText( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +/** + * ByTitle + */ +[@mel.module "@testing-library/dom"] +external _getByTitle: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + Dom.element = + "getByTitle"; + +let getByTitle = (~matcher, ~options=?, element) => + _getByTitle(element, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _getAllByTitle: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + array(Dom.element) = + "getAllByTitle"; + +let getAllByTitle = (~matcher, ~options=?, element) => + _getAllByTitle( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryByTitle: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + Js.null(Dom.element) = + "queryByTitle"; + +let queryByTitle = (~matcher, ~options=?, element) => + _queryByTitle( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryAllByTitle: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + array(Dom.element) = + "queryAllByTitle"; + +let queryAllByTitle = (~matcher, ~options=?, element) => + _queryAllByTitle( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findByTitle: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByTitle"; + +let findByTitle = (~matcher, ~options=?, element) => + _findByTitle(element, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _findAllByTitle: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByTitle"; + +let findAllByTitle = (~matcher, ~options=?, element) => + _findAllByTitle( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +/** + * ByDisplayValue + */ +[@mel.module "@testing-library/dom"] +external _getByDisplayValue: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + Dom.element = + "getByDisplayValue"; + +let getByDisplayValue = (~matcher, ~options=?, element) => + _getByDisplayValue( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _getAllByDisplayValue: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + array(Dom.element) = + "getAllByDisplayValue"; + +let getAllByDisplayValue = (~matcher, ~options=?, element) => + _getAllByDisplayValue( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryByDisplayValue: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + Js.null(Dom.element) = + "queryByDisplayValue"; + +let queryByDisplayValue = (~matcher, ~options=?, element) => + _queryByDisplayValue( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryAllByDisplayValue: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + array(Dom.element) = + "queryAllByDisplayValue"; + +let queryAllByDisplayValue = (~matcher, ~options=?, element) => + _queryAllByDisplayValue( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findByDisplayValue: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByDisplayValue"; + +let findByDisplayValue = (~matcher, ~options=?, element) => + _findByDisplayValue( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findAllByDisplayValue: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByDisplayValue"; + +let findAllByDisplayValue = (~matcher, ~options=?, element) => + _findAllByDisplayValue( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +/** + * ByRole + */ +[@mel.module "@testing-library/dom"] +external _getByRole: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + Dom.element = + "getByRole"; + +let getByRole = (~matcher, ~options=?, element) => + _getByRole(element, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _getAllByRole: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + array(Dom.element) = + "getAllByRole"; + +let getAllByRole = (~matcher, ~options=?, element) => + _getAllByRole( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryByRole: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + Js.null(Dom.element) = + "queryByRole"; + +let queryByRole = (~matcher, ~options=?, element) => + _queryByRole(element, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _queryAllByRole: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + array(Dom.element) = + "queryAllByRole"; + +let queryAllByRole = (~matcher, ~options=?, element) => + _queryAllByRole( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findByRole: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByRole"; + +let findByRole = (~matcher, ~options=?, element) => + _findByRole(element, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _findAllByRole: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByRole"; + +let findAllByRole = (~matcher, ~options=?, element) => + _findAllByRole( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +/** + * ByTestId + */ +[@mel.module "@testing-library/dom"] +external _getByTestId: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + Dom.element = + "getByTestId"; + +let getByTestId = (~matcher, ~options=?, element) => + _getByTestId(element, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _getAllByTestId: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + array(Dom.element) = + "getAllByTestId"; + +let getAllByTestId = (~matcher, ~options=?, element) => + _getAllByTestId( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryByTestId: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + Js.null(Dom.element) = + "queryByTestId"; + +let queryByTestId = (~matcher, ~options=?, element) => + _queryByTestId( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _queryAllByTestId: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + array(Dom.element) = + "queryAllByTestId"; + +let queryAllByTestId = (~matcher, ~options=?, element) => + _queryAllByTestId( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findByTestId: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByTestId"; + +let findByTestId = (~matcher, ~options=?, element) => + _findByTestId( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _findAllByTestId: + ( + Dom.element, + ~matcher: [@mel.unwrap] [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByTestId"; + +let findAllByTestId = (~matcher, ~options=?, element) => + _findAllByTestId( + element, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); diff --git a/test/melange-testing-library/dom/Utils.re b/test/melange-testing-library/dom/Utils.re new file mode 100644 index 000000000..5d6c999eb --- /dev/null +++ b/test/melange-testing-library/dom/Utils.re @@ -0,0 +1,176 @@ +module MutationObserver = { + type options = { + . + "attributeFilter": Js.undefined(array(string)), + "attributeOldValue": Js.undefined(bool), + "attributes": Js.undefined(bool), + "characterData": Js.undefined(bool), + "characterDataOldValue": Js.undefined(bool), + "childList": Js.undefined(bool), + "subtree": Js.undefined(bool), + }; + + [@mel.obj] + external makeOptions: + ( + ~attributeFilter: array(string)=?, + ~attributeOldValue: bool=?, + ~attributes: bool=?, + ~characterData: bool=?, + ~characterDataOldValue: bool=?, + ~childList: bool=?, + ~subtree: bool=?, + unit + ) => + options; +}; + +module WaitFor = { + type options = { + . + "container": Js.undefined(Dom.element), + "interval": Js.undefined(int), + "mutationObserverOptions": Js.undefined(MutationObserver.options), + "onTimeout": Js.undefined(Js.Exn.t => Js.Exn.t), + "showOriginalStackTrace": Js.undefined(bool), + "stackTraceError": Js.undefined(Js.Exn.t), + "timeout": Js.undefined(int), + }; + + [@mel.obj] + external makeOptions: + ( + ~container: Dom.element=?, + ~interval: int=?, + ~mutationObserverOptions: MutationObserver.options=?, + ~onTimeout: Js.Exn.t => Js.Exn.t=?, + ~showOriginalStackTrace: bool=?, + ~stackTraceError: Js.Exn.t=?, + ~timeout: int=?, + unit + ) => + options; +}; + +module WaitForElement = { + type options = { + . + "container": Js.undefined(Dom.element), + "timeout": Js.undefined(int), + }; + + [@mel.obj] + external makeOptions: + ( + ~container: Dom.element=?, + ~mutationObserverInit: MutationObserver.options=?, + ~timeout: int=?, + unit + ) => + options; +}; + +[@mel.module "@testing-library/dom"] +external _waitFor: + (unit => unit, Js.undefined(WaitFor.options)) => Js.Promise.t('a) = + "waitFor"; + +let waitFor = (~callback, ~options=?, ()) => + _waitFor(callback, Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _waitForPromise: + (unit => Js.Promise.t('a), Js.undefined(WaitFor.options)) => + Js.Promise.t('b) = + "waitFor"; + +let waitForPromise = (~callback, ~options=?, ()) => + _waitForPromise(callback, Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _waitForElement: + (Js.undefined(unit => 'a), Js.undefined(WaitForElement.options)) => + Js.Promise.t('a) = + "waitForElement"; + +let waitForElement = (~callback=?, ~options=?, ()) => + _waitForElement( + Js.Undefined.fromOption(callback), + Js.Undefined.fromOption(options), + ); + +[@mel.module "@testing-library/dom"] +external _waitForElementToBeRemoved: + ( + ~callback: [@mel.unwrap] [ | `Func(unit => 'a) | `Value('a)], + Js.undefined(WaitFor.options) + ) => + Js.Promise.t(unit) = + "waitForElementToBeRemoved"; + +let waitForElementToBeRemoved = (~callback, ~options=?, ()) => + _waitForElementToBeRemoved(~callback, Js.Undefined.fromOption(options)); + +[@mel.module "@testing-library/dom"] +external _prettyDOM: (Dom.element, Js.undefined(int)) => string = "prettyDOM"; + +let prettyDOM = (~maxLength=?, element) => + _prettyDOM(element, Js.Undefined.fromOption(maxLength)); + +[@mel.module "@testing-library/dom"] +external _logDOM: (Dom.element, Js.undefined(int)) => unit = "logDOM"; + +let logDOM = (~maxLength=?, element) => + _logDOM(element, Js.Undefined.fromOption(maxLength)); + +module Configure = { + type options = { + . + "_disableExpensiveErrorDiagnostics": Js.undefined(bool), + "asyncUtilTimeout": Js.undefined(int), + "asyncWrapper": Js.undefined(unit => unit), + "computedStyleSupportsPseudoElements": Js.undefined(bool), + "defaultHidden": Js.undefined(bool), + "eventWrapper": Js.undefined(unit => unit), + "getElementError": Js.undefined((string, Dom.element) => Js.Exn.t), + "showOriginalStackTrace": Js.undefined(bool), + "testIdAttribute": Js.undefined(string), + "throwSuggestions": Js.undefined(bool), + }; + + [@mel.obj] + external makeOptions: + ( + ~_disableExpensiveErrorDiagnostics: bool=?, + ~asyncUtilTimeout: int=?, + ~asyncWrapper: unit => unit=?, + ~computedStyleSupportsPseudoElements: bool=?, + ~defaultHidden: bool=?, + ~eventWrapper: unit => unit=?, + ~getElementError: (string, Dom.element) => Js.Exn.t=?, + ~showOriginalStackTrace: bool=?, + ~testIdAttribute: string=?, + ~throwSuggestions: bool=?, + unit + ) => + options; +}; + +[@mel.module "@testing-library/dom"] +external configureWithFn: (Js.t({..}) => Js.t({..})) => unit = "configure"; + +[@mel.module "@testing-library/dom"] +external configureWithObject: Configure.options => unit = "configure"; + +let configure = + ( + ~update: [ + | `Func(Configure.options => Configure.options) + | `Object(Configure.options) + ], + ) => { + switch (update) { + | `Func(fn) => configureWithFn(fn) + | `Object(obj) => configureWithObject(obj) + }; +}; diff --git a/test/melange-testing-library/dom/dune b/test/melange-testing-library/dom/dune new file mode 100644 index 000000000..8ae145e24 --- /dev/null +++ b/test/melange-testing-library/dom/dune @@ -0,0 +1,7 @@ +(library + (name melange_testing_library_dom) + (wrapped false) + (modes melange) + (libraries melange.dom) + (preprocess + (pps melange.ppx))) diff --git a/test/melange-testing-library/react/ReactTestingLibrary.re b/test/melange-testing-library/react/ReactTestingLibrary.re new file mode 100644 index 000000000..879157830 --- /dev/null +++ b/test/melange-testing-library/react/ReactTestingLibrary.re @@ -0,0 +1,1044 @@ +open DomTestingLibrary; + +module FireEvent = FireEvent; + +type renderResult; +type queries; +type renderOptions = { + . + "container": Js.undefined(Dom.element), + "baseElement": Js.undefined(Dom.element), + "hydrate": Js.undefined(bool), + "wrapper": Js.undefined(Dom.element), + "queries": Js.undefined(queries), +}; + +[@mel.module "@testing-library/react"] +external cleanup: unit => unit = "cleanup"; + +[@mel.module "@testing-library/react"] +external actAsync: (unit => Js.Promise.t('a)) => Js.Promise.t(unit) = "act"; + +[@mel.module "@testing-library/react"] +external _act: (unit => Js.undefined(Js.Promise.t('a))) => unit = "act"; + +let act = callback => + _act(() => { + callback(); + // (work-around) BuckleScript compiles `unit` to `0`, this will cause a warning as following: + // Warning: The callback passed to act(...) function must return undefined, or a Promise. + Js.Undefined.empty; + }); + +[@mel.module "@testing-library/react"] +external _render: (React.element, renderOptions) => renderResult = "render"; + +[@mel.get] external container: renderResult => Dom.element = "container"; + +[@mel.get] external baseElement: renderResult => Dom.element = "baseElement"; + +[@mel.send.pipe: renderResult] +external _debug: (Js.undefined(Dom.element), Js.undefined(int)) => unit = + "debug"; + +[@mel.send.pipe: renderResult] external unmount: unit => bool = "unmount"; + +[@mel.send.pipe: renderResult] +external rerender: React.element => unit = "rerender"; + +[@mel.send.pipe: renderResult] +external asFragment: unit => Dom.element = "asFragment"; + +// ByLabelText +[@mel.send.pipe: renderResult] +external _getByLabelText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + Dom.element = + "getByLabelText"; + +let getByLabelText = (~matcher, ~options=?, result) => + _getByLabelText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _getAllByLabelText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + array(Dom.element) = + "getAllByLabelText"; + +let getAllByLabelText = (~matcher, ~options=?, result) => + _getAllByLabelText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryByLabelText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + Js.null(Dom.element) = + "queryByLabelText"; + +let queryByLabelText = (~matcher, ~options=?, result) => + _queryByLabelText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryAllByLabelText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + array(Dom.element) = + "queryAllByLabelText"; + +let queryAllByLabelText = (~matcher, ~options=?, result) => + _queryAllByLabelText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findByLabelText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByLabelText"; + +let findByLabelText = (~matcher, ~options=?, result) => + _findByLabelText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findAllByLabelText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByLabelTextQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByLabelText"; + +let findAllByLabelText = (~matcher, ~options=?, result) => + _findAllByLabelText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +// ByPlaceholderText +[@mel.send.pipe: renderResult] +external _getByPlaceholderText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + Dom.element = + "getByPlaceholderText"; + +let getByPlaceholderText = (~matcher, ~options=?, result) => + _getByPlaceholderText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _getAllByPlaceholderText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + array(Dom.element) = + "getAllByPlaceholderText"; + +let getAllByPlaceholderText = (~matcher, ~options=?, result) => + _getAllByPlaceholderText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryByPlaceholderText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + Js.null(Dom.element) = + "queryByPlaceholderText"; + +let queryByPlaceholderText = (~matcher, ~options=?, result) => + _queryByPlaceholderText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryAllByPlaceholderText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + array(Dom.element) = + "queryAllByPlaceholderText"; + +let queryAllByPlaceholderText = (~matcher, ~options=?, result) => + _queryAllByPlaceholderText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findByPlaceholderText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByPlaceholderText"; + +let findByPlaceholderText = (~matcher, ~options=?, result) => + _findByPlaceholderText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findAllByPlaceholderText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByPlaceholderTextQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByPlaceholderText"; + +let findAllByPlaceholderText = (~matcher, ~options=?, result) => + _findAllByPlaceholderText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +// ByText +[@mel.send.pipe: renderResult] +external _getByText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + Dom.element = + "getByText"; + +let getByText = (~matcher, ~options=?, result) => + _getByText(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _getAllByText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + array(Dom.element) = + "getAllByText"; + +let getAllByText = (~matcher, ~options=?, result) => + _getAllByText(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _queryByText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + Js.null(Dom.element) = + "queryByText"; + +let queryByText = (~matcher, ~options=?, result) => + _queryByText(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _queryAllByText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + array(Dom.element) = + "queryAllByText"; + +let queryAllByText = (~matcher, ~options=?, result) => + _queryAllByText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findByText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByText"; + +let findByText = (~matcher, ~options=?, result) => + _findByText(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _findAllByText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTextQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByText"; + +let findAllByText = (~matcher, ~options=?, result) => + _findAllByText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +// ByAltText +[@mel.send.pipe: renderResult] +external _getByAltText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + Dom.element = + "getByAltText"; + +let getByAltText = (~matcher, ~options=?, result) => + _getByAltText(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _getAllByAltText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + array(Dom.element) = + "getAllByAltText"; + +let getAllByAltText = (~matcher, ~options=?, result) => + _getAllByAltText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryByAltText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + Js.null(Dom.element) = + "queryByAltText"; + +let queryByAltText = (~matcher, ~options=?, result) => + _queryByAltText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryAllByAltText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + array(Dom.element) = + "queryAllByAltText"; + +let queryAllByAltText = (~matcher, ~options=?, result) => + _queryAllByAltText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findByAltText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByAltText"; + +let findByAltText = (~matcher, ~options=?, result) => + _findByAltText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findAllByAltText: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByAltTextQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByAltText"; + +let findAllByAltText = (~matcher, ~options=?, result) => + _findAllByAltText( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +// ByTitle +[@mel.send.pipe: renderResult] +external _getByTitle: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + Dom.element = + "getByTitle"; + +let getByTitle = (~matcher, ~options=?, result) => + _getByTitle(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _getAllByTitle: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + array(Dom.element) = + "getAllByTitle"; + +let getAllByTitle = (~matcher, ~options=?, result) => + _getAllByTitle( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryByTitle: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + Js.null(Dom.element) = + "queryByTitle"; + +let queryByTitle = (~matcher, ~options=?, result) => + _queryByTitle(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _queryAllByTitle: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + array(Dom.element) = + "queryAllByTitle"; + +let queryAllByTitle = (~matcher, ~options=?, result) => + _queryAllByTitle( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findByTitle: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByTitle"; + +let findByTitle = (~matcher, ~options=?, result) => + _findByTitle(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _findAllByTitle: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTitleQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByTitle"; + +let findAllByTitle = (~matcher, ~options=?, result) => + _findAllByTitle( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +// ByDisplayValue +[@mel.send.pipe: renderResult] +external _getByDisplayValue: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + Dom.element = + "getByDisplayValue"; + +let getByDisplayValue = (~matcher, ~options=?, result) => + _getByDisplayValue( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _getAllByDisplayValue: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + array(Dom.element) = + "getAllByDisplayValue"; + +let getAllByDisplayValue = (~matcher, ~options=?, result) => + _getAllByDisplayValue( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryByDisplayValue: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + Js.null(Dom.element) = + "queryByDisplayValue"; + +let queryByDisplayValue = (~matcher, ~options=?, result) => + _queryByDisplayValue( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryAllByDisplayValue: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + array(Dom.element) = + "queryAllByDisplayValue"; + +let queryAllByDisplayValue = (~matcher, ~options=?, result) => + _queryAllByDisplayValue( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findByDisplayValue: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByDisplayValue"; + +let findByDisplayValue = (~matcher, ~options=?, result) => + _findByDisplayValue( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findAllByDisplayValue: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByDisplayValueQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByDisplayValue"; + +let findAllByDisplayValue = (~matcher, ~options=?, result) => + _findAllByDisplayValue( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +// ByRole +[@mel.send.pipe: renderResult] +external _getByRole: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + Dom.element = + "getByRole"; + +let getByRole = (~matcher, ~options=?, result) => + _getByRole(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _getAllByRole: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + array(Dom.element) = + "getAllByRole"; + +let getAllByRole = (~matcher, ~options=?, result) => + _getAllByRole(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _queryByRole: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + Js.null(Dom.element) = + "queryByRole"; + +let queryByRole = (~matcher, ~options=?, result) => + _queryByRole(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _queryAllByRole: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + array(Dom.element) = + "queryAllByRole"; + +let queryAllByRole = (~matcher, ~options=?, result) => + _queryAllByRole( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findByRole: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByRole"; + +let findByRole = (~matcher, ~options=?, result) => + _findByRole(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _findAllByRole: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByRoleQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByRole"; + +let findAllByRole = (~matcher, ~options=?, result) => + _findAllByRole( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +// ByTestId +[@mel.send.pipe: renderResult] +external _getByTestId: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + Dom.element = + "getByTestId"; + +let getByTestId = (~matcher, ~options=?, result) => + _getByTestId(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _getAllByTestId: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + array(Dom.element) = + "getAllByTestId"; + +let getAllByTestId = (~matcher, ~options=?, result) => + _getAllByTestId( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryByTestId: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + Js.null(Dom.element) = + "queryByTestId"; + +let queryByTestId = (~matcher, ~options=?, result) => + _queryByTestId( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _queryAllByTestId: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + array(Dom.element) = + "queryAllByTestId"; + +let queryAllByTestId = (~matcher, ~options=?, result) => + _queryAllByTestId( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +[@mel.send.pipe: renderResult] +external _findByTestId: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + Js.Promise.t(Dom.element) = + "findByTestId"; + +let findByTestId = (~matcher, ~options=?, result) => + _findByTestId(result, ~matcher, ~options=Js.Undefined.fromOption(options)); + +[@mel.send.pipe: renderResult] +external _findAllByTestId: + ( + ~matcher: + [@mel.unwrap] [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: Js.undefined(ByTestIdQuery.options) + ) => + Js.Promise.t(array(Dom.element)) = + "findAllByTestId"; + +let findAllByTestId = (~matcher, ~options=?, result) => + _findAllByTestId( + result, + ~matcher, + ~options=Js.Undefined.fromOption(options), + ); + +let render = + ( + ~baseElement=?, + ~container=?, + ~hydrate=?, + ~wrapper=?, + ~queries=?, + element, + ) => { + let baseElement_ = + switch (container) { + | Some(container') => Js.Undefined.return(container') + | None => Js.Undefined.fromOption(baseElement) + }; + let container_ = Js.Undefined.fromOption(container); + + _render( + element, + { + "baseElement": baseElement_, + "container": container_, + "hydrate": Js.Undefined.fromOption(hydrate), + "wrapper": Js.Undefined.fromOption(wrapper), + "queries": Js.Undefined.fromOption(queries), + }, + ); +}; + +let debug = (~el=?, ~maxLengthToPrint=?) => + _debug( + Js.Undefined.fromOption(el), + Js.Undefined.fromOption(maxLengthToPrint), + ); diff --git a/test/melange-testing-library/react/ReactTestingLibrary.rei b/test/melange-testing-library/react/ReactTestingLibrary.rei new file mode 100644 index 000000000..e2608f7e5 --- /dev/null +++ b/test/melange-testing-library/react/ReactTestingLibrary.rei @@ -0,0 +1,702 @@ +module FireEvent: { + let abort: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let animationEnd: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let animationIteration: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let animationStart: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let blur: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let canPlay: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let canPlayThrough: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let change: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let click: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let compositionEnd: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let compositionStart: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let compositionUpdate: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let contextMenu: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let copy: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let cut: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let dblClick: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let doubleClick: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let drag: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let dragEnd: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let dragEnter: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let dragExit: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let dragLeave: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let dragOver: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let dragStart: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let drop: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let durationChange: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let emptied: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let encrypted: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let ended: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let error: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let focus: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let focusIn: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let focusOut: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let input: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let invalid: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let keyDown: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let keyPress: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let keyUp: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let load: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let loadStart: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let loadedData: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let loadedMetadata: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let mouseDown: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let mouseEnter: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let mouseLeave: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let mouseMove: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let mouseOut: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let mouseOver: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let mouseUp: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let paste: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let pause: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let play: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let playing: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let progress: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let rateChange: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let scroll: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let seeked: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let seeking: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let select: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let stalled: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let submit: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let suspend: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let timeUpdate: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let touchCancel: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let touchEnd: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let touchMove: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let touchStart: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let transitionEnd: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let volumeChange: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let waiting: (~eventInit: Js.t({..})=?, Dom.element) => unit; + let wheel: (~eventInit: Js.t({..})=?, Dom.element) => unit; +}; + +type renderResult; +type queries; +type renderOptions = { + . + "container": Js.undefined(Dom.element), + "baseElement": Js.undefined(Dom.element), + "hydrate": Js.undefined(bool), + "wrapper": Js.undefined(Dom.element), + "queries": Js.undefined(queries), +}; + +[@mel.module "@testing-library/react"] +external cleanup: unit => unit = "cleanup"; + +[@mel.module "@testing-library/react"] +external actAsync: (unit => Js.Promise.t('a)) => Js.Promise.t(unit) = "act"; + +let act: (unit => unit) => unit; + +[@mel.get] external container: renderResult => Dom.element = "container"; + +[@mel.get] external baseElement: renderResult => Dom.element = "baseElement"; + +[@mel.send.pipe: renderResult] external unmount: unit => bool = "unmount"; + +[@mel.send.pipe: renderResult] +external asFragment: unit => Dom.element = "asFragment"; + +// ByLabelText +let getByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByLabelTextQuery.options=?, + renderResult + ) => + Dom.element; + +let getAllByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByLabelTextQuery.options=?, + renderResult + ) => + array(Dom.element); + +let queryByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByLabelTextQuery.options=?, + renderResult + ) => + Js.null(Dom.element); + +let queryAllByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByLabelTextQuery.options=?, + renderResult + ) => + array(Dom.element); + +let findByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByLabelTextQuery.options=?, + renderResult + ) => + Js.Promise.t(Dom.element); + +let findAllByLabelText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByLabelTextQuery.options=?, + renderResult + ) => + Js.Promise.t(array(Dom.element)); + +// ByPlaceholderText +let getByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByPlaceholderTextQuery.options=?, + renderResult + ) => + Dom.element; + +let getAllByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByPlaceholderTextQuery.options=?, + renderResult + ) => + array(Dom.element); + +let queryByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByPlaceholderTextQuery.options=?, + renderResult + ) => + Js.null(Dom.element); + +let queryAllByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByPlaceholderTextQuery.options=?, + renderResult + ) => + array(Dom.element); + +let findByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByPlaceholderTextQuery.options=?, + renderResult + ) => + Js.Promise.t(Dom.element); + +let findAllByPlaceholderText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByPlaceholderTextQuery.options=?, + renderResult + ) => + Js.Promise.t(array(Dom.element)); + +// ByText +let getByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByTextQuery.options=?, + renderResult + ) => + Dom.element; + +let getAllByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByTextQuery.options=?, + renderResult + ) => + array(Dom.element); + +let queryByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByTextQuery.options=?, + renderResult + ) => + Js.null(Dom.element); + +let queryAllByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByTextQuery.options=?, + renderResult + ) => + array(Dom.element); + +let findByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByTextQuery.options=?, + renderResult + ) => + Js.Promise.t(Dom.element); + +let findAllByText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByTextQuery.options=?, + renderResult + ) => + Js.Promise.t(array(Dom.element)); + +// ByAltText +let getByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByAltTextQuery.options=?, + renderResult + ) => + Dom.element; + +let getAllByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByAltTextQuery.options=?, + renderResult + ) => + array(Dom.element); + +let queryByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByAltTextQuery.options=?, + renderResult + ) => + Js.null(Dom.element); + +let queryAllByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByAltTextQuery.options=?, + renderResult + ) => + array(Dom.element); + +let findByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByAltTextQuery.options=?, + renderResult + ) => + Js.Promise.t(Dom.element); + +let findAllByAltText: + ( + ~matcher: [ + | `Func((string, Dom.element) => bool) + | `RegExp(Js.Re.t) + | `Str(string) + ], + ~options: DomTestingLibrary.ByAltTextQuery.options=?, + renderResult + ) => + Js.Promise.t(array(Dom.element)); + +// ByTitle +let getByTitle: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTitleQuery.options=?, + renderResult + ) => + Dom.element; + +let getAllByTitle: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTitleQuery.options=?, + renderResult + ) => + array(Dom.element); + +let queryByTitle: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTitleQuery.options=?, + renderResult + ) => + Js.null(Dom.element); + +let queryAllByTitle: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTitleQuery.options=?, + renderResult + ) => + array(Dom.element); + +let findByTitle: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTitleQuery.options=?, + renderResult + ) => + Js.Promise.t(Dom.element); + +let findAllByTitle: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTitleQuery.options=?, + renderResult + ) => + Js.Promise.t(array(Dom.element)); + +// ByDisplayValue +let getByDisplayValue: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByDisplayValueQuery.options=?, + renderResult + ) => + Dom.element; + +let getAllByDisplayValue: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByDisplayValueQuery.options=?, + renderResult + ) => + array(Dom.element); + +let queryByDisplayValue: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByDisplayValueQuery.options=?, + renderResult + ) => + Js.null(Dom.element); + +let queryAllByDisplayValue: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByDisplayValueQuery.options=?, + renderResult + ) => + array(Dom.element); + +let findByDisplayValue: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByDisplayValueQuery.options=?, + renderResult + ) => + Js.Promise.t(Dom.element); + +let findAllByDisplayValue: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByDisplayValueQuery.options=?, + renderResult + ) => + Js.Promise.t(array(Dom.element)); + +// ByRole +let getByRole: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByRoleQuery.options=?, + renderResult + ) => + Dom.element; + +let getAllByRole: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByRoleQuery.options=?, + renderResult + ) => + array(Dom.element); + +let queryByRole: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByRoleQuery.options=?, + renderResult + ) => + Js.null(Dom.element); + +let queryAllByRole: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByRoleQuery.options=?, + renderResult + ) => + array(Dom.element); + +let findByRole: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByRoleQuery.options=?, + renderResult + ) => + Js.Promise.t(Dom.element); + +let findAllByRole: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByRoleQuery.options=?, + renderResult + ) => + Js.Promise.t(array(Dom.element)); + +// ByTestId +let getByTestId: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTestIdQuery.options=?, + renderResult + ) => + Dom.element; + +let getAllByTestId: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTestIdQuery.options=?, + renderResult + ) => + array(Dom.element); + +let queryByTestId: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTestIdQuery.options=?, + renderResult + ) => + Js.null(Dom.element); + +let queryAllByTestId: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTestIdQuery.options=?, + renderResult + ) => + array(Dom.element); + +let findByTestId: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTestIdQuery.options=?, + renderResult + ) => + Js.Promise.t(Dom.element); + +let findAllByTestId: + ( + ~matcher: [ + | `Str(string) + | `RegExp(Js.Re.t) + | `Func((string, Dom.element) => bool) + ], + ~options: DomTestingLibrary.ByTestIdQuery.options=?, + renderResult + ) => + Js.Promise.t(array(Dom.element)); + +[@mel.send.pipe: renderResult] +external rerender: React.element => unit = "rerender"; + +let render: + ( + ~baseElement: Dom.element=?, + ~container: Dom.element=?, + ~hydrate: bool=?, + ~wrapper: Dom.element=?, + ~queries: queries=?, + React.element + ) => + renderResult; + +let debug: + (~el: Dom.element=?, ~maxLengthToPrint: int=?, renderResult) => unit; diff --git a/test/melange-testing-library/react/dune b/test/melange-testing-library/react/dune new file mode 100644 index 000000000..ecc0a0ab4 --- /dev/null +++ b/test/melange-testing-library/react/dune @@ -0,0 +1,7 @@ +(library + (name melange_testing_library_react) + (libraries reason-react melange_testing_library_dom) + (wrapped false) + (modes melange) + (preprocess + (pps melange.ppx))) From 4434bfd27d765a7734d26db18a5f0f9fb4894f42 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Mon, 18 Nov 2024 20:53:10 +0100 Subject: [PATCH 04/26] Fix Form__test with RTL --- package-lock.json | 94 +++++++--------------------------- package.json | 5 +- test/Form__test.re | 122 ++++++++++++++++++++++++--------------------- test/dune | 4 +- test/jest/Jest.re | 19 +++++++ 5 files changed, 107 insertions(+), 137 deletions(-) diff --git a/package-lock.json b/package-lock.json index b11818400..5a2861815 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,8 @@ }, "devDependencies": { "jest": "^26.0.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-test-renderer": "^18.2.0" + "react": "19.0.0-rc.1", + "react-dom": "19.0.0-rc.1" } }, "node_modules/@ampproject/remapping": { @@ -3802,17 +3801,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4143,15 +4131,6 @@ "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", "dev": true }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -4516,66 +4495,37 @@ "dev": true }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, + "version": "19.0.0-rc.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0-rc.1.tgz", + "integrity": "sha512-NZKln+uyPuyHchzP07I6GGYFxdAoaKhehgpCa3ltJGzwE31OYumLeshGaitA1R/fS5d9D2qpZVwTFAr6zCLM9w==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "19.0.0-rc.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0-rc.1.tgz", + "integrity": "sha512-k8MfDX+4G+eaa1cXXI9QF4d+pQtYol3nx8vauqRWUEOPqC7NQn2qmEqUsLoSd28rrZUL+R3T2VC+kZ2Hyx1geQ==", + "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "0.25.0-rc.1" }, "peerDependencies": { - "react": "^18.2.0" + "react": "19.0.0-rc.1" } }, + "node_modules/react-dom/node_modules/scheduler": { + "version": "0.25.0-rc.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc.1.tgz", + "integrity": "sha512-fVinv2lXqYpKConAMdergOl5owd0rY1O4P/QTe0aWKCqGtu7VsCt1iqQFxSJtqK4Lci/upVSBpGwVC7eWcuS9Q==", + "license": "MIT" + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, - "node_modules/react-shallow-renderer": { - "version": "16.15.0", - "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", - "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", - "dev": true, - "dependencies": { - "object-assign": "^4.1.1", - "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-test-renderer": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz", - "integrity": "sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==", - "dev": true, - "dependencies": { - "react-is": "^18.2.0", - "react-shallow-renderer": "^16.15.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-test-renderer/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -5089,14 +5039,6 @@ "node": ">=10" } }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", diff --git a/package.json b/package.json index 6020ed48a..a9bd27f37 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,8 @@ "homepage": "https://reasonml.github.io/reason-react/", "devDependencies": { "jest": "^26.0.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-test-renderer": "^18.2.0" + "react": "19.0.0-rc.1", + "react-dom": "19.0.0-rc.1" }, "jest": { "moduleDirectories": [ diff --git a/test/Form__test.re b/test/Form__test.re index 85593047b..2ea26d9c4 100644 --- a/test/Form__test.re +++ b/test/Form__test.re @@ -1,7 +1,4 @@ open Jest; -open Jest.Expect; -open ReactDOMTestUtils; -open Belt; module FormData = React.Experimental.FormData; @@ -25,7 +22,11 @@ module Thread = { let (optimisticMessages, addOptimisticMessage) = React.Experimental.useOptimistic(messages, (state, newMessage) => [ - {text: newMessage, sending: true, key: List.length(state) + 1}, + { + text: newMessage, + sending: true, + key: List.length(state) + 1, + }, ...state, ] ); @@ -49,24 +50,26 @@ module Thread = { }; }; <> - {{ - optimisticMessages->Belt.List.map(message => -
- {React.string(message.text)} - {message.sending - ? React.null - : {React.string("(Enviando...)")} } -
- ); - } - ->Belt.List.toArray - ->React.array} +
+ {{ + optimisticMessages->Belt.List.map(message => + + {React.string(message.text)} + {message.sending + ? React.null + : {React.string("(Enviando...)")} } + + ); + } + ->Belt.List.toArray + ->React.array} +
{React.cloneElement( ReactDOM.createElement( "form", ~props=ReactDOM.domProps(~ref=ReactDOM.Ref.domRef(formRef), ()), [| - , + , , |], ), @@ -84,7 +87,15 @@ module App = { [@react.component] let make = () => { let (messages, setMessages) = - React.useState(() => [{text: "¡Hola!", sending: false, key: 1}]); + React.useState(() => + [ + { + text: "Hola!", + sending: false, + key: 1, + }, + ] + ); let sendMessage = formData => { let formMessage = FormData.get("message", formData); @@ -95,7 +106,14 @@ module App = { | JSString(text) => let _ = setMessages(messages => - [{text, sending: true, key: 1}, ...messages] + [ + { + text, + sending: true, + key: 1, + }, + ...messages, + ] ); Js.Promise.resolve(); | _ => Js.Promise.resolve() @@ -108,46 +126,38 @@ module App = { }; }; -describe("Form with useOptimistic", () => { - let container = ref(None); - - beforeEach(prepareContainer(container)); - afterEach(cleanupContainer(container)); - - test("should render the form", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); +let (let.await) = (p, f) => Js.Promise.then_(f, p); +let (let.catch) = (p, f) => Js.Promise.then_(f, p); - act(() => ReactDOM.Client.render(root, )); +let findByString = (text, container) => + ReactTestingLibrary.findByText(~matcher=`Str(text), container); - expect( - container - ->DOM.findBySelectorAndTextContent("button", "0") - ->Option.isSome, - ) - ->toBe(true); +let findByPlaceholderText = (text, container) => + ReactTestingLibrary.findByPlaceholderText(~matcher=`Str(text), container); - let button = container->DOM.findBySelector("button"); +describe("Form with useOptimistic", () => { + testPromise("should render the form", finish => { + let container = ReactTestingLibrary.render(); + + ReactTestingLibrary.actAsync(() => { + let.await _ = findByString("Hola!", container); + + let.await button = findByString("Enviar", container); + let.await input = findByPlaceholderText("message", container); + + FireEvent.change( + input, + ~eventInit={ + "target": { + "value": "Let's go!", + }, + }, + ); - act(() => { - switch (button) { - | Some(button) => Simulate.click(button) - | None => () - } + FireEvent.click(button); + let.await _newMessage = findByString("Let's go!", container); + /* If the promise resolve, means the node is found in the DOM */ + finish(); }); - - expect( - container - ->DOM.findBySelectorAndTextContent("button", "0") - ->Option.isSome, - ) - ->toBe(false); - - expect( - container - ->DOM.findBySelectorAndTextContent("button", "1") - ->Option.isSome, - ) - ->toBe(true); - }); + }) }); diff --git a/test/dune b/test/dune index fd66b7258..c444622f1 100644 --- a/test/dune +++ b/test/dune @@ -8,7 +8,7 @@ reason-react.node jest melange.belt - melange-testing-library.dom - melange-testing-library.react) + melange_testing_library_dom + melange_testing_library_react) (preprocess (pps melange.ppx reason-react-ppx))) diff --git a/test/jest/Jest.re b/test/jest/Jest.re index 3c9a6bc5f..fa3a29742 100644 --- a/test/jest/Jest.re +++ b/test/jest/Jest.re @@ -22,6 +22,25 @@ let testAsync: (~timeout: int=?, string, (unit => unit) => unit) => unit = (~timeout=?, name, f) => testAsyncU(name, callback => f(callback), timeout); +external testPromiseU: + ( + string, + (unit => Js.Promise.t(unit)) => Js.Promise.t(unit), + option(int) + ) => + unit = + "test"; + +let testPromise: + ( + ~timeout: int=?, + string, + (unit => Js.Promise.t(unit)) => Js.Promise.t(unit) + ) => + unit = + (~timeout=?, name, f) => + testPromiseU(name, callback => f(callback), timeout); + module Only = { [@mel.scope "describe"] external describeU: (string, (. unit) => unit) => unit = "only"; From ab43c4b410d8d428148f47416d27528af4402631 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 11:38:45 +0100 Subject: [PATCH 05/26] Start migrating Hooks__test --- src/ReactTestRenderer.re | 24 -- src/ReactTestRenderer.rei | 24 -- src/dune | 1 - test/Hooks__test.re | 493 ++++++++++++-------------------- test/ReactTestRenderer__test.re | 106 ------- test/dune | 1 + 6 files changed, 190 insertions(+), 459 deletions(-) delete mode 100644 src/ReactTestRenderer.re delete mode 100644 src/ReactTestRenderer.rei delete mode 100644 test/ReactTestRenderer__test.re diff --git a/src/ReactTestRenderer.re b/src/ReactTestRenderer.re deleted file mode 100644 index 2c3494447..000000000 --- a/src/ReactTestRenderer.re +++ /dev/null @@ -1,24 +0,0 @@ -type t; - -[@mel.module "react-test-renderer"] -external create: React.element => t = "create"; - -[@mel.send] external toJSON: t => Js.Json.t = "toJSON"; -[@mel.send] external toObject: t => Js.t({..}) = "%identity"; - -module Shallow = { - type t; - - [@mel.module "react-test-renderer/shallow"] - external createRenderer: unit => t = "createRenderer"; - - [@mel.send] - external render: (t, React.element) => option(React.element) = "render"; - - [@mel.send] - external getRenderOutput: t => option(React.element) = "getRenderOutput"; - - [@mel.send] external unmount: t => unit = "unmount"; - - let renderWithRenderer = render(createRenderer()); -}; diff --git a/src/ReactTestRenderer.rei b/src/ReactTestRenderer.rei deleted file mode 100644 index c68d57b71..000000000 --- a/src/ReactTestRenderer.rei +++ /dev/null @@ -1,24 +0,0 @@ -type t; - -[@mel.module "react-test-renderer"] -external create: React.element => t = "create"; - -[@mel.send] external toJSON: t => Js.Json.t = "toJSON"; -[@mel.send] external toObject: t => Js.t({..}) = "%identity"; - -module Shallow: { - type t; - - [@mel.module "react-test-renderer/shallow"] - external createRenderer: unit => t = "createRenderer"; - - [@mel.send] - external render: (t, React.element) => option(React.element) = "render"; - - [@mel.send] - external getRenderOutput: t => option(React.element) = "getRenderOutput"; - - [@mel.send] external unmount: t => unit = "unmount"; - - let renderWithRenderer: React.element => option(React.element); -}; diff --git a/src/dune b/src/dune index 1226d33d9..91ba8d459 100644 --- a/src/dune +++ b/src/dune @@ -9,7 +9,6 @@ ReactDOM ReactDOMServer ReactDOMTestUtils - ReactTestRenderer ReasonReactRouter ReasonReactErrorBoundary) (preprocess diff --git a/test/Hooks__test.re b/test/Hooks__test.re index 9e8ff3dfe..19f1d6307 100644 --- a/test/Hooks__test.re +++ b/test/Hooks__test.re @@ -1,7 +1,5 @@ open Jest; open Jest.Expect; -open ReactDOMTestUtils; -open Belt; /* https://react.dev/blog/2022/03/08/react-18-upgrade-guide#configuring-your-testing-environment */ [%%mel.raw "globalThis.IS_REACT_ACT_ENVIRONMENT = true"]; @@ -47,7 +45,11 @@ let store = (initialState: 'a) => { }; }; - {getState, setState, subscribe}; + { + getState, + setState, + subscribe, + }; }; module DummyStatefulComponent = { @@ -161,311 +163,194 @@ module DummyComponentWithRefAndEffect = { }; }; -describe("Hooks", () => { - let container = ref(None); - - beforeEach(prepareContainer(container)); - afterEach(cleanupContainer(container)); - - test("can render react components", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => ReactDOM.Client.render(root, )); - - expect( - container - ->DOM.findBySelectorAndTextContent("button", "0") - ->Option.isSome, - ) - ->toBe(true); - - let button = container->DOM.findBySelector("button"); - - act(() => { - switch (button) { - | Some(button) => Simulate.click(button) - | None => () - } - }); - - expect( - container - ->DOM.findBySelectorAndTextContent("button", "0") - ->Option.isSome, - ) - ->toBe(false); - - expect( - container - ->DOM.findBySelectorAndTextContent("button", "1") - ->Option.isSome, - ) - ->toBe(true); - }); - - test("can render react components with reducers", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => {ReactDOM.Client.render(root, )}); - - expect( - container - ->DOM.findBySelectorAndTextContent(".value", "0") - ->Option.isSome, - ) - ->toBe(true); - - let button = - container->DOM.findBySelectorAndPartialTextContent( - "button", - "Increment", - ); - - act(() => { - switch (button) { - | Some(button) => Simulate.click(button) - | None => () - } - }); - - expect( - container - ->DOM.findBySelectorAndTextContent(".value", "0") - ->Option.isSome, - ) - ->toBe(false); - - expect( - container - ->DOM.findBySelectorAndTextContent(".value", "1") - ->Option.isSome, - ) - ->toBe(true); - - let button = - container->DOM.findBySelectorAndPartialTextContent( - "button", - "Decrement", - ); - - act(() => { - switch (button) { - | Some(button) => Simulate.click(button) - | None => () - } - }); - - expect( - container - ->DOM.findBySelectorAndTextContent(".value", "0") - ->Option.isSome, - ) - ->toBe(true); - - expect( - container - ->DOM.findBySelectorAndTextContent(".value", "1") - ->Option.isSome, - ) - ->toBe(false); - }); - - test("can render react components with reducers (map state)", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render(root, ) - }); - - expect( - container - ->DOM.findBySelectorAndTextContent(".value", "1") - ->Option.isSome, - ) - ->toBe(true); - - let button = - container->DOM.findBySelectorAndPartialTextContent( - "button", - "Increment", - ); - - act(() => { - switch (button) { - | Some(button) => Simulate.click(button) - | None => () - } - }); - - expect( - container - ->DOM.findBySelectorAndTextContent(".value", "1") - ->Option.isSome, - ) - ->toBe(false); - - expect( - container - ->DOM.findBySelectorAndTextContent(".value", "2") - ->Option.isSome, - ) - ->toBe(true); - - let button = - container->DOM.findBySelectorAndPartialTextContent( - "button", - "Decrement", - ); - - act(() => { - switch (button) { - | Some(button) => Simulate.click(button) - | None => () - } - }); - - expect( - container - ->DOM.findBySelectorAndTextContent(".value", "1") - ->Option.isSome, - ) - ->toBe(true); - - expect( - container - ->DOM.findBySelectorAndTextContent(".value", "2") - ->Option.isSome, - ) - ->toBe(false); - }); - - test("can render react components with effects", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - let callback = Mock.fn(); - - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); - - expect(callback->Mock.getMock->Mock.calls)->toEqual([|[|0|], [|1|]|]); - }); - - test("can render react components with layout effects", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - let callback = Mock.fn(); +let (let.await) = (p, f) => Js.Promise.then_(f, p); - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); +let findByString = (text, container) => + ReactTestingLibrary.findByText(~matcher=`Str(text), container); - expect(callback->Mock.getMock->Mock.calls)->toEqual([|[|0|], [|1|]|]); - }); +let findByPlaceholderText = (text, container) => + ReactTestingLibrary.findByPlaceholderText(~matcher=`Str(text), container); - test("can work with useRef", () => { - let container = getContainer(container); - let myRef = ref(None); - let callback = reactRef => { - myRef := Some(reactRef); - }; - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); +[@mel.get] external tagName: Dom.element => string = "tagName"; +[@mel.get] external innerHTML: Dom.element => string = "innerHTML"; +type domTokenList; +[@mel.get] external classList: Dom.element => domTokenList = "classList"; +[@mel.send] external contains: (domTokenList, string) => bool = "contains"; - expect(myRef.contents->Option.map(item => item.current)) - ->toEqual(Some(2)); - }); - - testAsync("useSyncExternalStore", finish => { - let {subscribe, getState, setState} = store("initial"); - let mock = Mock.fn(); - - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - let subscribeWithMock = args => { - let _ = mock(.); - subscribe(args); - }; - - module App = { - [@react.component] - let make = () => { - let snapshot = - React.useSyncExternalStore( - ~subscribe=subscribeWithMock, - ~getSnapshot=getState, - ); - {React.string(snapshot)} ; - }; - }; - - act(() => ReactDOM.Client.render(root, )); - - /* Ensure initial value is passed */ - expect( - container - ->DOM.findBySelectorAndTextContent("span", "initial") - ->Option.isSome, - ) - ->toBe(true); - - act(() => setState("changed")); - - /* Ensure store is being updated */ - let nextState = getState(); - expect(nextState)->toBe("changed"); - - /* on next render */ - act(() => ReactDOM.Client.render(root, )); +let findByClass = (tag, container) => { + let fn = (_string, element: Dom.element) => { + element->classList->contains(tag); + }; + ReactTestingLibrary.findByText(~matcher=`Func(fn), container); +}; - /* the state should be propagated to App */ - expect( - container - ->DOM.findBySelectorAndTextContent("span", "changed") - ->Option.isSome, - ) - ->toBe(true); +let findByTag = (tag, container) => { + let fn = (_string, element: Dom.element) => { + element->tagName->Js.String.toLowerCase == tag; + }; + ReactTestingLibrary.findByText(~matcher=`Func(fn), container); +}; - finish(); - }); +describe("Hooks", () => { + testPromise("can render react components", finish + => { + let container = ReactTestingLibrary.render(); + + ReactTestingLibrary.actAsync(() => { + let.await button = findByTag("button", container); + expect(button->innerHTML)->toBe("0"); + + FireEvent.click(button); + + expect(button->innerHTML)->toBe("1"); + finish(); + }); + }) + /* testPromise("can render react components with reducers", finish => { + let container = ReactTestingLibrary.render(); + + ReactTestingLibrary.actAsync(() => { + let.await value = findByClass("value", container); + expect(value->innerHTML)->toBe("0"); + + let.await incrementButton = findByString("Increment", container); + FireEvent.click(incrementButton); + expect(value->innerHTML)->toBe("1"); + + let.await decrementButton = findByString("Decrement", container); + FireEvent.click(decrementButton); + expect(value->innerHTML)->toBe("0"); + finish(); + }); + }); */ + /* testPromise("can render react components with reducers (map state)", finish => { + let container = + ReactTestingLibrary.render(); + + ReactTestingLibrary.actAsync(() => { + let.await value = findByClass("value", container); + expect(value->innerHTML)->toBe("0"); + + let.await incrementButton = findByString("Increment", container); + FireEvent.click(incrementButton); + expect(value->innerHTML)->toBe("2"); + + let.await decrementButton = findByString("Decrement", container); + FireEvent.click(decrementButton); + expect(value->innerHTML)->toBe("1"); + + finish(); + }); + }); */ + /* testPromise("can render react components with effects", finish => { + let callback = Mock.fn(); + + let container = + ReactTestingLibrary.render( + , + ); + + act(() => { + ReactDOM.Client.render( + root, + , + ) + }); + act(() => { + ReactDOM.Client.render( + root, + , + ) + }); + + expect(callback->Mock.getMock->Mock.calls)->toEqual([|[|0|], [|1|]|]); + }); */ + /* testPromise("can render react components with layout effects", finish => { + let callback = Mock.fn(); + + let container = + ReactTestingLibrary.render( + , + ); + + act(() => { + ReactDOM.Client.render( + root, + , + ) + }); + act(() => { + ReactDOM.Client.render( + root, + , + ) + }); + + expect(callback->Mock.getMock->Mock.calls)->toEqual([|[|0|], [|1|]|]); + }); */ + /* testPromise("can work with useRef", finish => { + let myRef = ref(None); + let callback = reactRef => { + myRef := Some(reactRef); + }; + + let container = + ReactTestingLibrary.render(); + + expect(myRef.contents->Option.map(item => item.current)) + ->toEqual(Some(2)); + }); */ + /* testAsync("useSyncExternalStore", finish => { + let {subscribe, getState, setState} = store("initial"); + let mock = Mock.fn(); + + let subscribeWithMock = args => { + let _ = mock(.); + subscribe(args); + }; + + module App = { + [@react.component] + let make = () => { + let snapshot = + React.useSyncExternalStore( + ~subscribe=subscribeWithMock, + ~getSnapshot=getState, + ); + {React.string(snapshot)} ; + }; + }; + + let container = ReactTestingLibrary.render(); + + /* Ensure initial value is passed */ + /* ReactTestingLibrary.actAsync(() => { + expect(container->findByString("initial"))->toBeInTheDocument() + }); */ + expect( + container + ->DOM.findBySelectorAndTextContent("span", "initial") + ->Option.isSome, + ) + ->toBe(true); + + act(() => setState("changed")); + + /* Ensure store is being updated */ + let nextState = getState(); + expect(nextState)->toBe("changed"); + + /* on next render */ + act(() => ReactDOM.Client.render(root, )); + + /* the state should be propagated to App */ + expect( + container + ->DOM.findBySelectorAndTextContent("span", "changed") + ->Option.isSome, + ) + ->toBe(true); + + finish(); + }); */ }); diff --git a/test/ReactTestRenderer__test.re b/test/ReactTestRenderer__test.re deleted file mode 100644 index 2db87305d..000000000 --- a/test/ReactTestRenderer__test.re +++ /dev/null @@ -1,106 +0,0 @@ -open Jest; - -module Tester = { - [@react.component] - let make = () =>
{React.string("Tester")}
; -}; - -describe("ReactTestRenderer", () => { - open Expect; - - test("create returns ReactTestInstance", () => { - let component = ReactTestRenderer.create(); - let keys = Js.Obj.keys(ReactTestRenderer.toObject(component)); - - expect(keys) - ->toEqual( - arrayContaining([| - "_Scheduler", - "root", - "toJSON", - "toTree", - "update", - "unmount", - "unstable_flushSync", - "getInstance", - |]), - ); - }); - - test("toJSON returns test rendered JSON", () => { - let component = ReactTestRenderer.create(); - let json = ReactTestRenderer.toJSON(component); - let expected = - Js.Json.parseExn( - {| - { - "type": "div", - "props": {}, - "children": [ "Tester" ] - } - |}, - ); - expect(json == expected)->toBe(true); - }); -}); - -let element =
{React.string("Tester")}
; - -describe("reactShallowRenderer", () => { - open Expect; - - test("createRenderer", () => { - let renderer = ReactTestRenderer.Shallow.createRenderer(); - let isDefined = - renderer - |> Js.Undefined.return - |> Js.Undefined.toOption - |> Option.is_some; - expect(isDefined)->toBe(true); - }); - - test("render accepts renderer", () => { - let renderer = ReactTestRenderer.Shallow.createRenderer(); - let render = ReactTestRenderer.Shallow.render(renderer); - expect(Js.typeof(render))->toEqual("function"); - }); - - test("render will render a component", () => { - let renderer = ReactTestRenderer.Shallow.createRenderer(); - let component = - ReactTestRenderer.Shallow.render(renderer, )->Option.get; - expect(component == element)->toBe(true); - }); - - test("renderWithRenderer will render a component", () => { - let component = - ReactTestRenderer.Shallow.renderWithRenderer()->Option.get; - - expect(component == element)->toBe(true); - }); - - test("getRenderOutput returns element", () => { - let renderer = ReactTestRenderer.Shallow.createRenderer(); - - ReactTestRenderer.Shallow.render(renderer, ) |> ignore; - - let component = - ReactTestRenderer.Shallow.getRenderOutput(renderer)->Option.get; - - expect(component == element)->toBe(true); - }); - - test("unmount removes the node", () => { - let renderer = ReactTestRenderer.Shallow.createRenderer(); - - ReactTestRenderer.Shallow.render(renderer, ) |> ignore; - ReactTestRenderer.Shallow.unmount(renderer); - - let component = - ReactTestRenderer.Shallow.getRenderOutput(renderer) - ->Option.get - ->Js.Null.return; - - expect(component)->toEqual(Js.null); - }); -}); diff --git a/test/dune b/test/dune index c444622f1..0dde05c81 100644 --- a/test/dune +++ b/test/dune @@ -8,6 +8,7 @@ reason-react.node jest melange.belt + melange.dom melange_testing_library_dom melange_testing_library_react) (preprocess From 8234b694e7e0f4af19ad965d46d4436777785690 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 11:40:11 +0100 Subject: [PATCH 06/26] Remove dependency --- dune-project | 6 ------ reason-react.opam | 1 - 2 files changed, 7 deletions(-) diff --git a/dune-project b/dune-project index 427e00e73..fbeb92d80 100644 --- a/dune-project +++ b/dune-project @@ -41,12 +41,6 @@ (reason (>= 3.12.0)) (ocaml-lsp-server :with-dev-setup) - (melange-testing-library - (and - (= "0.1.0") - :with-test - :with-dev-setup - :post)) (opam-check-npm-deps (and (= 1.0.0) diff --git a/reason-react.opam b/reason-react.opam index 13fb0a5f3..e2329051c 100644 --- a/reason-react.opam +++ b/reason-react.opam @@ -23,7 +23,6 @@ depends: [ "reason-react-ppx" {= version} "reason" {>= "3.12.0"} "ocaml-lsp-server" {with-dev-setup} - "melange-testing-library" {= "0.1.0" & with-test & with-dev-setup & post} "opam-check-npm-deps" {= "1.0.0" & with-dev-setup} "ocamlformat" {= "0.24.0" & with-dev-setup} "odoc" {with-doc} From 49b35e94a267614510e7ab5f59110271b6ac6830 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 11:41:25 +0100 Subject: [PATCH 07/26] Remove unused code from Form__test --- test/Form__test.re | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Form__test.re b/test/Form__test.re index 2ea26d9c4..b71c632e6 100644 --- a/test/Form__test.re +++ b/test/Form__test.re @@ -127,7 +127,6 @@ module App = { }; let (let.await) = (p, f) => Js.Promise.then_(f, p); -let (let.catch) = (p, f) => Js.Promise.then_(f, p); let findByString = (text, container) => ReactTestingLibrary.findByText(~matcher=`Str(text), container); From 3ca3ba147107cf6344c63e1a7d03403f9404f3d2 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 19:35:51 +0100 Subject: [PATCH 08/26] Add a jest-devtoolsgs --- Makefile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Makefile b/Makefile index 3512619a7..b0202c817 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,10 @@ jest: ## Run the jest unit tests jest-watch: ## Run the jest unit tests in watch mode @npx jest --watch +.PHONY: jest-devtools +jest-devtools: ## Run the jest unit tests in watch mode + @node --inspect-brk node_modules/.bin/jest --runInBand --detectOpenHandles + .PHONY: test test: ## Run the runtests from dune (snapshot) @$(DUNE) build @runtest @@ -62,3 +66,11 @@ create-switch: ## Create a local opam switch .PHONY: init init: create-switch install ## Create a local opam switch, install deps + +.PHONY: demo-watch +demo-watch: ## Build the demo in watch mode + @$(DUNE) build @melange-app --watch + +.PHONY: demo-serve +demo-serve: ## Build the demo and serve it + npx http-server -p 8080 _build/default/demo/ From 41ba9c2b2ffc0751697382d2675e7f6b534a389b Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 19:35:58 +0100 Subject: [PATCH 09/26] Add a jest-devtools --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index b0202c817..42d419313 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ jest-watch: ## Run the jest unit tests in watch mode .PHONY: jest-devtools jest-devtools: ## Run the jest unit tests in watch mode + @echo "open Chrome and go to chrome://inspect" @node --inspect-brk node_modules/.bin/jest --runInBand --detectOpenHandles .PHONY: test From c7998a9873c589db135bf1912b919d852c60d544 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 19:57:19 +0100 Subject: [PATCH 10/26] Migrate Hooks and Form into RTL --- test/Form__test.re | 3 - test/Hooks__test.re | 406 ++++++++++-------- test/ReactRouter__test.re | 12 +- test/React__test.re | 10 +- .../dom/DomTestingLibrary.rei | 7 +- test/melange-testing-library/dom/Utils.re | 10 +- 6 files changed, 255 insertions(+), 193 deletions(-) diff --git a/test/Form__test.re b/test/Form__test.re index b71c632e6..256081650 100644 --- a/test/Form__test.re +++ b/test/Form__test.re @@ -2,9 +2,6 @@ open Jest; module FormData = React.Experimental.FormData; -/* https://react.dev/blog/2022/03/08/react-18-upgrade-guide#configuring-your-testing-environment */ -[%%mel.raw "globalThis.IS_REACT_ACT_ENVIRONMENT = true"]; - type message = { text: string, sending: bool, diff --git a/test/Hooks__test.re b/test/Hooks__test.re index 19f1d6307..23d78826f 100644 --- a/test/Hooks__test.re +++ b/test/Hooks__test.re @@ -1,9 +1,6 @@ open Jest; open Jest.Expect; -/* https://react.dev/blog/2022/03/08/react-18-upgrade-guide#configuring-your-testing-environment */ -[%%mel.raw "globalThis.IS_REACT_ACT_ENVIRONMENT = true"]; - type store('a) = { subscribe: (unit => unit, unit) => unit, getState: unit => 'a, @@ -56,10 +53,39 @@ module DummyStatefulComponent = { [@react.component] let make = (~initialValue=0, ()) => { let (value, setValue) = React.useState(() => initialValue); + let onClick = _ => setValue(value => value + 1); + ; + }; +}; + +module DummyIncrementReducerComponent = { + type action = + | Increment; - ; + [@react.component] + let make = (~initialValue=0) => { + let (state, send) = + React.useReducer( + (state, action) => + switch (action) { + | Increment => state + 1 + }, + initialValue, + ); + + Js.log2("state", state); + +
+
{React.int(state)}
+ +
; }; }; @@ -67,8 +93,9 @@ module DummyReducerComponent = { type action = | Increment | Decrement; + [@react.component] - let make = (~initialValue=0, ()) => { + let make = (~initialValue=0) => { let (state, send) = React.useReducer( (state, action) => @@ -79,13 +106,15 @@ module DummyReducerComponent = { initialValue, ); + Js.log2("state", state); + <> -
state->React.int
+
{React.int(state)}
; }; @@ -109,7 +138,7 @@ module DummyReducerWithMapStateComponent = { ); <> -
state->React.int
+
state->React.int
@@ -120,9 +149,9 @@ module DummyReducerWithMapStateComponent = { }; }; -module DummyComponentWithEffect = { +module WithEffect = { [@react.component] - let make = (~value=0, ~callback, ()) => { + let make = (~value, ~callback) => { React.useEffect1( () => { callback(value); @@ -131,7 +160,26 @@ module DummyComponentWithEffect = { [|value|], ); -
; + {React.int(value)} ; + }; +}; + +module RerenderOnEachClick = { + [@react.component] + let make = (~initialValue=0, ~maxValue=3, ~callback) => { + let (value, setValue) = React.useState(() => initialValue); + let onClick = _ => + if (value < maxValue) { + setValue(value => value + 1); + } else { + /* Fire a setState with the same value */ + setValue(value => value); + }; + +
+

{React.string("RerenderOnEachClick")}

+ +
; }; }; @@ -152,7 +200,7 @@ module DummyComponentWithLayoutEffect = { module DummyComponentWithRefAndEffect = { [@react.component] - let make = (~callback, ()) => { + let make = (~callback) => { let myRef = React.useRef(1); React.useEffect0(() => { myRef.current = myRef.current + 1; @@ -165,8 +213,8 @@ module DummyComponentWithRefAndEffect = { let (let.await) = (p, f) => Js.Promise.then_(f, p); -let findByString = (text, container) => - ReactTestingLibrary.findByText(~matcher=`Str(text), container); +let getByString = (text, container) => + ReactTestingLibrary.getByText(~matcher=`Str(text), container); let findByPlaceholderText = (text, container) => ReactTestingLibrary.findByPlaceholderText(~matcher=`Str(text), container); @@ -184,173 +232,175 @@ let findByClass = (tag, container) => { ReactTestingLibrary.findByText(~matcher=`Func(fn), container); }; -let findByTag = (tag, container) => { +let getByTag = (tag, container) => { let fn = (_string, element: Dom.element) => { element->tagName->Js.String.toLowerCase == tag; }; - ReactTestingLibrary.findByText(~matcher=`Func(fn), container); + ReactTestingLibrary.getByText(~matcher=`Func(fn), container); +}; + +let getByInnerHTML = (text, container) => { + let fn = (inner, _element: Dom.element) => { + String.equal(inner, text); + }; + ReactTestingLibrary.getByText(~matcher=`Func(fn), container); }; describe("Hooks", () => { - testPromise("can render react components", finish - => { - let container = ReactTestingLibrary.render(); - - ReactTestingLibrary.actAsync(() => { - let.await button = findByTag("button", container); - expect(button->innerHTML)->toBe("0"); - - FireEvent.click(button); - - expect(button->innerHTML)->toBe("1"); - finish(); - }); - }) - /* testPromise("can render react components with reducers", finish => { - let container = ReactTestingLibrary.render(); - - ReactTestingLibrary.actAsync(() => { - let.await value = findByClass("value", container); - expect(value->innerHTML)->toBe("0"); - - let.await incrementButton = findByString("Increment", container); - FireEvent.click(incrementButton); - expect(value->innerHTML)->toBe("1"); - - let.await decrementButton = findByString("Decrement", container); - FireEvent.click(decrementButton); - expect(value->innerHTML)->toBe("0"); - finish(); - }); - }); */ - /* testPromise("can render react components with reducers (map state)", finish => { - let container = - ReactTestingLibrary.render(); - - ReactTestingLibrary.actAsync(() => { - let.await value = findByClass("value", container); - expect(value->innerHTML)->toBe("0"); - - let.await incrementButton = findByString("Increment", container); - FireEvent.click(incrementButton); - expect(value->innerHTML)->toBe("2"); - - let.await decrementButton = findByString("Decrement", container); - FireEvent.click(decrementButton); - expect(value->innerHTML)->toBe("1"); - - finish(); - }); - }); */ - /* testPromise("can render react components with effects", finish => { - let callback = Mock.fn(); - - let container = - ReactTestingLibrary.render( - , - ); - - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); - - expect(callback->Mock.getMock->Mock.calls)->toEqual([|[|0|], [|1|]|]); - }); */ - /* testPromise("can render react components with layout effects", finish => { - let callback = Mock.fn(); - - let container = - ReactTestingLibrary.render( - , - ); - - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); - act(() => { - ReactDOM.Client.render( - root, - , - ) - }); - - expect(callback->Mock.getMock->Mock.calls)->toEqual([|[|0|], [|1|]|]); - }); */ - /* testPromise("can work with useRef", finish => { - let myRef = ref(None); - let callback = reactRef => { - myRef := Some(reactRef); - }; - - let container = - ReactTestingLibrary.render(); - - expect(myRef.contents->Option.map(item => item.current)) - ->toEqual(Some(2)); - }); */ - /* testAsync("useSyncExternalStore", finish => { - let {subscribe, getState, setState} = store("initial"); - let mock = Mock.fn(); - - let subscribeWithMock = args => { - let _ = mock(.); - subscribe(args); - }; - - module App = { - [@react.component] - let make = () => { - let snapshot = - React.useSyncExternalStore( - ~subscribe=subscribeWithMock, - ~getSnapshot=getState, - ); - {React.string(snapshot)} ; - }; - }; - - let container = ReactTestingLibrary.render(); - - /* Ensure initial value is passed */ - /* ReactTestingLibrary.actAsync(() => { - expect(container->findByString("initial"))->toBeInTheDocument() - }); */ - expect( - container - ->DOM.findBySelectorAndTextContent("span", "initial") - ->Option.isSome, - ) - ->toBe(true); - - act(() => setState("changed")); - - /* Ensure store is being updated */ - let nextState = getState(); - expect(nextState)->toBe("changed"); - - /* on next render */ - act(() => ReactDOM.Client.render(root, )); - - /* the state should be propagated to App */ - expect( - container - ->DOM.findBySelectorAndTextContent("span", "changed") - ->Option.isSome, - ) - ->toBe(true); + beforeEach(() => ReactTestingLibrary.cleanup()); + + test("can render react components", () => { + let container = ReactTestingLibrary.render(); + let button = getByTag("button", container); + expect(DomTestingLibrary.getNodeText(button))->toBe("0"); + FireEvent.click(button); + expect(DomTestingLibrary.getNodeText(button))->toBe("1"); + }); + + test("can render react components with effects", () => { + let container = + ReactTestingLibrary.render( + ()} />, + ); + let button = getByTag("button", container); + let counter = getByTag("span", container); + FireEvent.click(button); /* 0 -> 1 */ + expect(DomTestingLibrary.getNodeText(counter))->toBe("1"); + FireEvent.click(button); /* 1 -> 2 */ + expect(DomTestingLibrary.getNodeText(counter))->toBe("2"); + /* Limit reached, counter doesn't increase, so no rerenders */ + FireEvent.click(button); /* 2 -> 2 */ + expect(DomTestingLibrary.getNodeText(counter))->toBe("2"); + }); + + test("can render react components with effects", () => { + let callback = Mock.fn(); + + let container = + ReactTestingLibrary.render( + , + ); + let button = getByTag("button", container); + FireEvent.click(button); /* 0 -> 1 */ + FireEvent.click(button); /* 1 -> 2 */ + /* Limit reached, counter doesn't increase, so no rerenders */ + FireEvent.click(button); /* 2 -> 2 */ + FireEvent.click(button); /* 2 -> 2 */ + + let allCalls = callback->Mock.getMock->Mock.calls; + expect(allCalls)->toEqual([|[|0|], [|1|], [|2|]|]); + }); + + test("useSyncExternalStore", () => { + let mock = Mock.fn(); + let {subscribe, getState, setState} = store("initial"); + + let subscribeWithMock = args => { + let _ = mock(.); + subscribe(args); + }; + + module App = { + [@react.component] + let make = () => { + let snapshot = + React.useSyncExternalStore( + ~subscribe=subscribeWithMock, + ~getSnapshot=getState, + ); + {React.string(snapshot)} ; + }; + }; + + let container = ReactTestingLibrary.render(); + + /* Ensure initial value is passed */ + let span = getByTag("span", container); + expect(DomTestingLibrary.getNodeText(span))->toBe("initial"); + + ReactTestingLibrary.act(() => setState("changed")); + + /* Ensure store is being updated */ + let nextState = getState(); + expect(nextState)->toBe("changed"); + + /* the state should be propagated to App */ + expect(DomTestingLibrary.getNodeText(span))->toBe("changed"); + + /* on the next render */ + ReactTestingLibrary.rerender(, container); + + /* the state should stay 'changed' */ + expect(DomTestingLibrary.getNodeText(span))->toBe("changed"); + }); + + test("can render react components with layout effects", () => { + let callback = Mock.fn(); + + let container = + ReactTestingLibrary.render( + , + ); + + ReactTestingLibrary.act(() => { + ReactTestingLibrary.rerender( + , + container, + ) + }); + ReactTestingLibrary.act(() => { + ReactTestingLibrary.rerender( + , + container, + ) + }); + expect(callback->Mock.getMock->Mock.calls)->toEqual([|[|0|], [|1|]|]); + }); + + test("can work with useRef", () => { + let myRef = ref(None); + let callback = reactRef => { + myRef := Some(reactRef); + }; + + let _ = + ReactTestingLibrary.render(); + + switch (myRef.contents) { + | Some(ref) => expect(ref.current)->toBe(2) + | None => failwith("no ref") + }; + }); + /* Reducer tests are disabled */ + /* test("can render react components with reducers", () => { + let container = + ReactTestingLibrary.render( + , + ); + + let counter = getByTag("main", container); + let button = getByTag("button", container); + expect(counter->innerHTML)->toBe("0"); + FireEvent.click(button); + expect(counter->innerHTML)->toBe("1"); + }); */ + /* testPromise("can render react components with reducers (map state)", finish => { + let container = + ReactTestingLibrary.render(); + + ReactTestingLibrary.actAsync(() => { + let.await value = findByClass("value", container); + expect(value->innerHTML)->toBe("0"); + + let.await incrementButton = findByString("Increment", container); + FireEvent.click(incrementButton); + expect(value->innerHTML)->toBe("2"); + + let.await decrementButton = findByString("Decrement", container); + FireEvent.click(decrementButton); + expect(value->innerHTML)->toBe("1"); finish(); - }); */ + }); + }); */ }); diff --git a/test/ReactRouter__test.re b/test/ReactRouter__test.re index ed0900d1d..f69471371 100644 --- a/test/ReactRouter__test.re +++ b/test/ReactRouter__test.re @@ -4,14 +4,22 @@ open ReasonReactRouter; describe("it allows to create url from string", () => { test("it supports basic paths", () => { - let expected = {path: ["foo", "bar"], hash: "", search: ""}; + let expected = { + path: ["foo", "bar"], + hash: "", + search: "", + }; let generated = dangerouslyGetInitialUrl(~serverUrlString="/foo/bar", ()); expect(generated == expected)->toBe(true); }); test("it creates with search", () => { - let expected = {path: ["foo", "bar"], hash: "", search: "q=term"}; + let expected = { + path: ["foo", "bar"], + hash: "", + search: "q=term", + }; let generated = dangerouslyGetInitialUrl(~serverUrlString="/foo/bar?q=term", ()); diff --git a/test/React__test.re b/test/React__test.re index bfa76985e..31ede8c9d 100644 --- a/test/React__test.re +++ b/test/React__test.re @@ -24,7 +24,10 @@ module DummyComponentThatMapsChildren = { {children->React.Children.mapWithIndex((element, index) => { React.cloneElement( element, - {"key": string_of_int(index), "data-index": index}, + { + "key": string_of_int(index), + "data-index": index, + }, ) })}
; @@ -318,7 +321,10 @@ describe("React", () => { act(() => { ReactDOM.Client.render( root, - render({name: "Joe", imageUrl: "https://foo.png"}), + render({ + name: "Joe", + imageUrl: "https://foo.png", + }), ) }); diff --git a/test/melange-testing-library/dom/DomTestingLibrary.rei b/test/melange-testing-library/dom/DomTestingLibrary.rei index f8989f68b..0ec173659 100644 --- a/test/melange-testing-library/dom/DomTestingLibrary.rei +++ b/test/melange-testing-library/dom/DomTestingLibrary.rei @@ -186,13 +186,10 @@ module WaitForElement: { options; }; -let waitFor: - (~callback: unit => unit, ~options: WaitFor.options=?, unit) => - Js.Promise.t('a); +let waitFor: (~options: WaitFor.options=?, unit => unit) => Js.Promise.t('a); let waitForPromise: - (~callback: unit => Js.Promise.t('a), ~options: WaitFor.options=?, unit) => - Js.Promise.t('b); + (~options: WaitFor.options=?, unit => Js.Promise.t('a)) => Js.Promise.t('b); let waitForElement: (~callback: unit => 'a=?, ~options: WaitForElement.options=?, unit) => diff --git a/test/melange-testing-library/dom/Utils.re b/test/melange-testing-library/dom/Utils.re index 5d6c999eb..b4de999ed 100644 --- a/test/melange-testing-library/dom/Utils.re +++ b/test/melange-testing-library/dom/Utils.re @@ -75,7 +75,7 @@ external _waitFor: (unit => unit, Js.undefined(WaitFor.options)) => Js.Promise.t('a) = "waitFor"; -let waitFor = (~callback, ~options=?, ()) => +let waitFor = (~options=?, callback) => _waitFor(callback, Js.Undefined.fromOption(options)); [@mel.module "@testing-library/dom"] @@ -84,7 +84,7 @@ external _waitForPromise: Js.Promise.t('b) = "waitFor"; -let waitForPromise = (~callback, ~options=?, ()) => +let waitForPromise = (~options=?, callback) => _waitForPromise(callback, Js.Undefined.fromOption(options)); [@mel.module "@testing-library/dom"] @@ -102,7 +102,11 @@ let waitForElement = (~callback=?, ~options=?, ()) => [@mel.module "@testing-library/dom"] external _waitForElementToBeRemoved: ( - ~callback: [@mel.unwrap] [ | `Func(unit => 'a) | `Value('a)], + ~callback: + [@mel.unwrap] [ + | `Func(unit => 'a) + | `Value('a) + ], Js.undefined(WaitFor.options) ) => Js.Promise.t(unit) = From 5e05f2e2700579f45db33dc999ee8ccec15b56e9 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 19:57:45 +0100 Subject: [PATCH 11/26] Add demo to manually test easily --- demo/dune | 9 + demo/index.html | 42 ++++ demo/main.re | 187 ++++++++++++++++ dune | 2 +- package-lock.json | 528 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 9 +- 6 files changed, 758 insertions(+), 19 deletions(-) create mode 100644 demo/dune create mode 100644 demo/index.html create mode 100644 demo/main.re diff --git a/demo/dune b/demo/dune new file mode 100644 index 000000000..3f1c1ac86 --- /dev/null +++ b/demo/dune @@ -0,0 +1,9 @@ +(melange.emit + (target demo) + (alias melange-app) + (module_systems + (es6 mjs)) + (libraries reason-react jest melange.belt melange.dom) + (runtime_deps index.html) + (preprocess + (pps melange.ppx reason-react-ppx))) diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 000000000..e333157fd --- /dev/null +++ b/demo/index.html @@ -0,0 +1,42 @@ + + + + + + + + Demo reason-react + + + + + + +
+ + + + diff --git a/demo/main.re b/demo/main.re new file mode 100644 index 000000000..8994a58dc --- /dev/null +++ b/demo/main.re @@ -0,0 +1,187 @@ +module Stateful = { + [@react.component] + let make = (~title, ~initialValue=0, ~children=React.null) => { + let (value, setValue) = React.useState(() => initialValue); + let onClick = _ => setValue(value => value + 1); + +
+

{React.string(title)}

+ + children +
; + }; +}; + +module Reducer = { + type action = + | Increment + | Decrement; + + [@react.component] + let make = (~initialValue=0) => { + let (state, send) = + React.useReducer( + (state, action) => + switch (action) { + | Increment => state + 1 + | Decrement => state - 1 + }, + initialValue, + ); + + Js.log2("Reducer state", state); + +
+

{React.string("React.useReducer")}

+
state->React.int
+ + +
; + }; +}; + +module ReducerWithMapState = { + type action = + | Increment + | Decrement; + + [@react.component] + let make = (~initialValue=0) => { + let (state, send) = + React.useReducerWithMapState( + (state, action) => + switch (action) { + | Increment => state + 1 + | Decrement => state - 1 + }, + initialValue, + initialValue => initialValue + 75, + ); + + Js.log2("ReducerWithMapState state", state); + +
+

{React.string("React.useReducerWithMapState")}

+
state->React.int
+ + +
; + }; +}; + +module WithEffect = { + [@react.component] + let make = (~value) => { + React.useEffect1( + () => { + Js.log("useEffect"); + None; + }, + [|value|], + ); + + React.string("React.useEffect"); + }; +}; + +module RerenderOnEachClick = { + [@react.component] + let make = (~value=0, ~callback as _) => { + let (value, setValue) = React.useState(() => value); + let onClick = _ => + if (value < 3) { + Js.log2("Clicked with:", value); + setValue(value => value + 1); + } else { + Js.log("Max value reached, not firing a rerender"); + setValue(value => value); + }; + +
+

{React.string("RerenderOnEachClick")}

+ +
; + }; +}; + +module WithLayoutEffect = { + [@react.component] + let make = (~value=0, ~callback) => { + React.useLayoutEffect1( + () => { + callback(value); + Js.log("useLayoutEffect"); + None; + }, + [|value|], + ); + +

{React.string("React.useLayoutEffect")}

; + }; +}; + +module WithRefAndEffect = { + [@react.component] + let make = (~callback) => { + let myRef = React.useRef(1); + React.useEffect0(() => { + myRef.current = myRef.current + 1; + callback(myRef); + None; + }); + +
+

{React.string("React.useRef and useEffect")}

+
{React.int(myRef.current)}
+
; + }; +}; + +[@mel.module "react"] +external useReducer: + ([@mel.uncurry] (('state, 'action) => 'state), 'state) => + ('state, 'action => unit) = + "useReducer"; + +module UseReducerNoProblemo = { + [@react.component] + let make = () => { + let reducer = (v, _) => v + 1; + let (state, send) = useReducer(reducer, 0); + Js.log("asdfasd"); + ; + }; +}; + +module App = { + [@react.component] + let make = (~initialValue) => { + let value = 99; + let callback = _number => (); + +
+ + + + + + + +
; + }; +}; + +switch (ReactDOM.querySelector("#root")) { +| Some(el) => + let root = ReactDOM.Client.createRoot(el); + ReactDOM.Client.render(root, ); +| None => Js.log("No root element found") +}; diff --git a/dune b/dune index dc5b54052..5a812ba9c 100644 --- a/dune +++ b/dune @@ -1 +1 @@ -(dirs src test ppx) +(dirs src test ppx demo) diff --git a/package-lock.json b/package-lock.json index 5a2861815..b08e74e85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,10 @@ "name": "reason-react", "version": "0.11.0", "license": "MIT", - "dependencies": { - "@testing-library/dom": "^10.4.0", - "@testing-library/react": "^16.0.1" - }, "devDependencies": { + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.0.1", + "http-server": "^14.1.1", "jest": "^26.0.1", "react": "19.0.0-rc.1", "react-dom": "19.0.0-rc.1" @@ -35,6 +34,7 @@ "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, "dependencies": { "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" @@ -47,6 +47,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -58,6 +59,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -71,6 +73,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -78,12 +81,14 @@ "node_modules/@babel/code-frame/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -92,6 +97,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -100,6 +106,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -288,6 +295,7 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -319,6 +327,7 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -332,6 +341,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -343,6 +353,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -356,6 +367,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -363,12 +375,14 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -377,6 +391,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -385,6 +400,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -555,6 +571,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dev": true, "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -956,6 +973,7 @@ "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.10.4", @@ -975,6 +993,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -987,6 +1006,7 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1", @@ -1001,6 +1021,7 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5" @@ -1037,6 +1058,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, "license": "MIT" }, "node_modules/@types/babel__core": { @@ -1232,6 +1254,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -1240,6 +1263,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1276,6 +1300,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, "license": "Apache-2.0", "dependencies": { "dequal": "^2.0.3" @@ -1326,6 +1351,16 @@ "node": ">=0.10.0" } }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1488,6 +1523,19 @@ "node": ">=0.10.0" } }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1583,6 +1631,26 @@ "node": ">=0.10.0" } }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1637,6 +1705,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1811,6 +1880,7 @@ "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, "dependencies": { "color-name": "~1.1.4" }, @@ -1821,7 +1891,8 @@ "node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/combined-stream": { "version": "1.0.8", @@ -1862,6 +1933,16 @@ "node": ">=0.10.0" } }, + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1964,6 +2045,24 @@ "node": ">=0.10.0" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -1990,6 +2089,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2017,6 +2117,7 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, "license": "MIT" }, "node_modules/domexception": { @@ -2082,6 +2183,29 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2152,6 +2276,13 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, "node_modules/exec-sh": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", @@ -2449,6 +2580,27 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2505,10 +2657,14 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gensync": { "version": "1.0.0-beta.2", @@ -2528,6 +2684,26 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -2590,6 +2766,19 @@ "node": ">=4" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2619,10 +2808,50 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -2686,6 +2915,29 @@ "node": ">=0.10.0" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -2710,6 +2962,21 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", @@ -2724,6 +2991,73 @@ "node": ">= 6" } }, + "node_modules/http-server": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + }, + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-server/node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-server/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-server/node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -3659,7 +3993,8 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/js-yaml": { "version": "3.14.1", @@ -3814,6 +4149,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, "license": "MIT", "bin": { "lz-string": "bin/bin.js" @@ -3916,6 +4252,19 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3980,6 +4329,19 @@ "node": ">=0.10.0" } }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4216,6 +4578,19 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -4264,6 +4639,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, "node_modules/p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -4426,6 +4811,31 @@ "node": ">=8" } }, + "node_modules/portfinder": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -4488,6 +4898,22 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", + "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -4498,6 +4924,7 @@ "version": "19.0.0-rc.1", "resolved": "https://registry.npmjs.org/react/-/react-19.0.0-rc.1.tgz", "integrity": "sha512-NZKln+uyPuyHchzP07I6GGYFxdAoaKhehgpCa3ltJGzwE31OYumLeshGaitA1R/fS5d9D2qpZVwTFAr6zCLM9w==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4507,6 +4934,7 @@ "version": "19.0.0-rc.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0-rc.1.tgz", "integrity": "sha512-k8MfDX+4G+eaa1cXXI9QF4d+pQtYol3nx8vauqRWUEOPqC7NQn2qmEqUsLoSd28rrZUL+R3T2VC+kZ2Hyx1geQ==", + "dev": true, "license": "MIT", "dependencies": { "scheduler": "0.25.0-rc.1" @@ -4519,12 +4947,14 @@ "version": "0.25.0-rc.1", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc.1.tgz", "integrity": "sha512-fVinv2lXqYpKConAMdergOl5owd0rY1O4P/QTe0aWKCqGtu7VsCt1iqQFxSJtqK4Lci/upVSBpGwVC7eWcuS9Q==", + "dev": true, "license": "MIT" }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true }, "node_modules/read-pkg": { "version": "5.2.0", @@ -4580,6 +5010,7 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, "license": "MIT" }, "node_modules/regex-not": { @@ -4718,6 +5149,13 @@ "node": "6.* || >= 7.*" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, "node_modules/safe-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", @@ -5039,6 +5477,13 @@ "node": ">=10" } }, + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true, + "license": "MIT" + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5054,6 +5499,24 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -5118,6 +5581,25 @@ "dev": true, "optional": true }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -5604,6 +6086,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -5801,6 +6284,18 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -5919,6 +6414,13 @@ "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", diff --git a/package.json b/package.json index a9bd27f37..f6f991768 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,10 @@ "devDependencies": { "jest": "^26.0.1", "react": "19.0.0-rc.1", - "react-dom": "19.0.0-rc.1" + "react-dom": "19.0.0-rc.1", + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.0.1", + "http-server": "^14.1.1" }, "jest": { "moduleDirectories": [ @@ -39,9 +42,5 @@ "testMatch": [ "**/*__test.bs.js" ] - }, - "dependencies": { - "@testing-library/dom": "^10.4.0", - "@testing-library/react": "^16.0.1" } } From f8a08c3a1c8c3dd1b6475d014017ac27716d264c Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 20:10:23 +0100 Subject: [PATCH 12/26] Use Uncurried for tests --- test/Hooks__test.re | 121 +++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 69 deletions(-) diff --git a/test/Hooks__test.re b/test/Hooks__test.re index 23d78826f..71aa3dc4a 100644 --- a/test/Hooks__test.re +++ b/test/Hooks__test.re @@ -52,8 +52,8 @@ let store = (initialState: 'a) => { module DummyStatefulComponent = { [@react.component] let make = (~initialValue=0, ()) => { - let (value, setValue) = React.useState(() => initialValue); - let onClick = _ => setValue(value => value + 1); + let (value, setValue) = React.Uncurried.useState(() => initialValue); + let onClick = _ => setValue(. value => value + 1); ; }; }; @@ -65,7 +65,7 @@ module DummyIncrementReducerComponent = { [@react.component] let make = (~initialValue=0) => { let (state, send) = - React.useReducer( + React.Uncurried.useReducer( (state, action) => switch (action) { | Increment => state + 1 @@ -73,16 +73,9 @@ module DummyIncrementReducerComponent = { initialValue, ); - Js.log2("state", state); -
{React.int(state)}
-
; @@ -97,7 +90,7 @@ module DummyReducerComponent = { [@react.component] let make = (~initialValue=0) => { let (state, send) = - React.useReducer( + React.Uncurried.useReducer( (state, action) => switch (action) { | Increment => state + 1 @@ -110,10 +103,10 @@ module DummyReducerComponent = { <>
{React.int(state)}
- - ; @@ -124,10 +117,11 @@ module DummyReducerWithMapStateComponent = { type action = | Increment | Decrement; + [@react.component] let make = (~initialValue=0, ()) => { let (state, send) = - React.useReducerWithMapState( + React.Uncurried.useReducerWithMapState( (state, action) => switch (action) { | Increment => state + 1 @@ -137,15 +131,15 @@ module DummyReducerWithMapStateComponent = { initialValue => initialValue + 1, ); - <> -
state->React.int
- - - ; + ; }; }; @@ -167,13 +161,13 @@ module WithEffect = { module RerenderOnEachClick = { [@react.component] let make = (~initialValue=0, ~maxValue=3, ~callback) => { - let (value, setValue) = React.useState(() => initialValue); + let (value, setValue) = React.Uncurried.useState(() => initialValue); let onClick = _ => if (value < maxValue) { - setValue(value => value + 1); + setValue(. value => value + 1); } else { /* Fire a setState with the same value */ - setValue(value => value); + setValue(. value => value); };
@@ -211,8 +205,6 @@ module DummyComponentWithRefAndEffect = { }; }; -let (let.await) = (p, f) => Js.Promise.then_(f, p); - let getByString = (text, container) => ReactTestingLibrary.getByText(~matcher=`Str(text), container); @@ -221,16 +213,6 @@ let findByPlaceholderText = (text, container) => [@mel.get] external tagName: Dom.element => string = "tagName"; [@mel.get] external innerHTML: Dom.element => string = "innerHTML"; -type domTokenList; -[@mel.get] external classList: Dom.element => domTokenList = "classList"; -[@mel.send] external contains: (domTokenList, string) => bool = "contains"; - -let findByClass = (tag, container) => { - let fn = (_string, element: Dom.element) => { - element->classList->contains(tag); - }; - ReactTestingLibrary.findByText(~matcher=`Func(fn), container); -}; let getByTag = (tag, container) => { let fn = (_string, element: Dom.element) => { @@ -239,6 +221,10 @@ let getByTag = (tag, container) => { ReactTestingLibrary.getByText(~matcher=`Func(fn), container); }; +let getByRole = (role, container) => { + ReactTestingLibrary.getByRole(~matcher=`Str(role), container); +}; + let getByInnerHTML = (text, container) => { let fn = (inner, _element: Dom.element) => { String.equal(inner, text); @@ -371,36 +357,33 @@ describe("Hooks", () => { | None => failwith("no ref") }; }); - /* Reducer tests are disabled */ - /* test("can render react components with reducers", () => { - let container = - ReactTestingLibrary.render( - , - ); - - let counter = getByTag("main", container); - let button = getByTag("button", container); - expect(counter->innerHTML)->toBe("0"); - FireEvent.click(button); - expect(counter->innerHTML)->toBe("1"); - }); */ - /* testPromise("can render react components with reducers (map state)", finish => { - let container = - ReactTestingLibrary.render(); - - ReactTestingLibrary.actAsync(() => { - let.await value = findByClass("value", container); - expect(value->innerHTML)->toBe("0"); - - let.await incrementButton = findByString("Increment", container); - FireEvent.click(incrementButton); - expect(value->innerHTML)->toBe("2"); - - let.await decrementButton = findByString("Decrement", container); - FireEvent.click(decrementButton); - expect(value->innerHTML)->toBe("1"); - - finish(); - }); - }); */ + + test("can render react components with reducers", () => { + let container = + ReactTestingLibrary.render( + , + ); + + let counter = getByTag("main", container); + let button = getByTag("button", container); + expect(counter->innerHTML)->toBe("0"); + FireEvent.click(button); + expect(counter->innerHTML)->toBe("1"); + }); + + test("can render react components with reducers and initial map state", () => { + let container = + ReactTestingLibrary.render(); + + let counter = getByRole("Counter", container); + expect(counter->innerHTML)->toBe("1"); + + let incrementButton = getByRole("Increment", container); + FireEvent.click(incrementButton); + expect(counter->innerHTML)->toBe("2"); + + let decrementButton = getByRole("Decrement", container); + FireEvent.click(decrementButton); + expect(counter->innerHTML)->toBe("1"); + }); }); From 8d156d7e84924ddc55d6732e736cb64b81a71839 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 20:42:46 +0100 Subject: [PATCH 13/26] Migrate all React__test --- test/React__test.re | 399 ++++++++++++++++---------------------------- 1 file changed, 146 insertions(+), 253 deletions(-) diff --git a/test/React__test.re b/test/React__test.re index 31ede8c9d..e7b624355 100644 --- a/test/React__test.re +++ b/test/React__test.re @@ -1,10 +1,5 @@ open Jest; -open Jest.Expect; -open ReactDOMTestUtils; -open Belt; - -/* https://react.dev/blog/2022/03/08/react-18-upgrade-guide#configuring-your-testing-environment */ -[%%mel.raw "globalThis.IS_REACT_ACT_ENVIRONMENT = true"]; +open Expect; module ComponentThatThrows = { exception TestError; @@ -26,7 +21,7 @@ module DummyComponentThatMapsChildren = { element, { "key": string_of_int(index), - "data-index": index, + "role": Int.to_string(index), }, ) })} @@ -45,151 +40,102 @@ module DummyContext = { [@react.component] let make = () => { let value = React.useContext(context); -
value->React.int
; +
value->React.int
; }; }; }; -describe("React", () => { - let container = ref(None); +[@mel.get] external tagName: Dom.element => string = "tagName"; +[@mel.get] external innerHTML: Dom.element => string = "innerHTML"; - beforeEach(prepareContainer(container)); - afterEach(cleanupContainer(container)); +let getByRole = (role, container) => { + ReactTestingLibrary.getByRole(~matcher=`Str(role), container); +}; +let getByTag = (tag, container) => { + let fn = (_string, element: Dom.element) => { + element->tagName->Js.String.toLowerCase == tag; + }; + ReactTestingLibrary.getByText(~matcher=`Func(fn), container); +}; + +[@mel.send] +external getAttribute: (Dom.element, string) => option(string) = + "getAttribute"; + +describe("React", () => { test("can render DOM elements", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render(root,
"Hello world!"->React.string
) - }); - - expect( - container - ->DOM.findBySelectorAndTextContent("div", "Hello world!") - ->Option.isSome, - ) - ->toBe(true); + let container = + ReactTestingLibrary.render(
); + let div = getByRole("display", container); + expect(div->innerHTML)->toBe(""); }); test("can render null elements", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => ReactDOM.Client.render(root,
React.null
)); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div", "") - ->Option.isSome, - ) - ->toBe(true); + let container = + ReactTestingLibrary.render(
React.null
); + let div = getByRole("display", container); + expect(div->innerHTML)->toBe(""); }); test("can render string elements", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render(root,
"Hello"->React.string
) - }); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div", "Hello") - ->Option.isSome, - ) - ->toBe(true); + let container = + ReactTestingLibrary.render( +
"Hello world!"->React.string
, + ); + let div = getByRole("display", container); + expect(div->innerHTML)->toBe("Hello world!"); }); test("can render int elements", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => ReactDOM.Client.render(root,
12345->React.int
)); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div", "12345") - ->Option.isSome, - ) - ->toBe(true); + let container = + ReactTestingLibrary.render( +
12345->React.int
, + ); + let div = getByRole("display", container); + expect(div->innerHTML)->toBe("12345"); }); test("can render float elements", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render(root,
12.345->React.float
) - }); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div", "12.345") - ->Option.isSome, - ) - ->toBe(true); + let container = + ReactTestingLibrary.render( +
12.345->React.float
, + ); + let div = getByRole("display", container); + expect(div->innerHTML)->toBe("12.345"); }); test("can render array of elements", () => { - let container = getContainer(container); let array = - [|1, 2, 3|] - ->Array.map(item => { -
item->React.int
- }); - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render(root,
array->React.array
) - }); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div", "1") - ->Option.isSome, - ) - ->toBe(true); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div", "2") - ->Option.isSome, - ) - ->toBe(true); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div", "3") - ->Option.isSome, - ) - ->toBe(true); + Array.map( + item => { +
+ item->React.int +
+ }, + [|1, 2, 3|], + ); + let container = + ReactTestingLibrary.render(
{React.array(array)}
); + let div = getByRole("1", container); + expect(div->innerHTML)->toBe("1"); + let div = getByRole("2", container); + expect(div->innerHTML)->toBe("2"); + let div = getByRole("3", container); + expect(div->innerHTML)->toBe("3"); }); - test("can clone an element", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render( - root, + test("can clone an element with data-* attributes", () => { + let container = + ReactTestingLibrary.render( React.cloneElement(
"Hello"->React.string
, {"data-name": "World"}, ), - ) - }); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent( - "div[data-name='World']", - "Hello", - ) - ->Option.isSome, - ) - ->toBe(true); + ); + let output = ReactTestingLibrary.container(container)->innerHTML; + let found = Js.String.includes(~search="data-name=\"World\"", output); + expect(found)->toBe(true); }); test("can work with React refs", () => { @@ -200,112 +146,70 @@ describe("React", () => { }); test("Children", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render( - root, + let container = + ReactTestingLibrary.render(
1->React.int
2->React.int
3->React.int
, - ) - }); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div[data-index='0']", "1") - ->Option.isSome, - ) - ->toBe(true); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div[data-index='1']", "2") - ->Option.isSome, - ) - ->toBe(true); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div[data-index='2']", "3") - ->Option.isSome, - ) - ->toBe(true); + ); + + let div = getByRole("0", container); + expect(div->innerHTML)->toBe("1"); + let div = getByRole("1", container); + expect(div->innerHTML)->toBe("2"); + let div = getByRole("2", container); + expect(div->innerHTML)->toBe("3"); }); test("Context", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render( - root, + let container = + ReactTestingLibrary.render( , - ) - }); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div", "10") - ->Option.isSome, - ) - ->toBe(true); + ); + + expect(getByRole("value", container)->innerHTML)->toBe("10"); }); test("Events", () => { - let container = getContainer(container); let value = ref(""); - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render( - root, + let container = + ReactTestingLibrary.render( {value := event->React.Event.Form.target##value}} />, - ) - }); - - switch (container->DOM.findBySelector("input[name='test-input']")) { - | Some(input) => input->Simulate.changeWithValue("My value") - | None => () - }; - - expect(value.contents)->toEqual("My value"); + ); + + let input = getByRole("input", container); + FireEvent.change( + input, + ~eventInit={ + "target": { + "value": "Let's go!", + }, + }, + ); + + expect(value.contents)->toEqual("Let's go!"); }); test("React.Fragment with key", () => { - let container = getContainer(container); let title = Some("foo"); - let root = ReactDOM.Client.createRoot(container); - - act(() => { - ReactDOM.Client.render( - root, + let container = + ReactTestingLibrary.render( -
"Child"->React.string
+
"Child"->React.string
, - ) - }); - - expect( - container - ->DOM.findBySelectorAndPartialTextContent("div", "Child") - ->Option.isSome, - ) - ->toBe(true); + ); + + expect(getByRole("child", container)->innerHTML)->toBe("Child"); }); test("Type inference with keys", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - module Author = { type t = { name: string, @@ -318,27 +222,23 @@ describe("React", () => {
; - act(() => { - ReactDOM.Client.render( - root, + let container = + ReactTestingLibrary.render( render({ name: "Joe", imageUrl: "https://foo.png", }), - ) - }); - - expect(container->DOM.findBySelector("img")->Option.isSome)->toBe(true); + ); + let image = getByRole("img", container); + expect(image->getAttribute("src"))->toEqual(Some("https://foo.png")); }); - try( - test("ErrorBoundary", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); + test("ErrorBoundary + Suspense", () => { + [%mel.raw "console.error = () => {}"] |> ignore; - act(() => { - ReactDOM.Client.render( - root, + try({ + let container = + ReactTestingLibrary.render( { expect( @@ -346,31 +246,23 @@ describe("React", () => { ->Js.String.includes(~search="ComponentThatThrows"), ) ->toBe(true); - "An error occured"->React.string ; + "An error occured"->React.string ; }}> , - ) - }); - - expect( - container - ->DOM.findBySelectorAndTextContent("strong", "An error occured") - ->Option.isSome, - ) - ->toBe(true); - }) - ) { - | _error => - /* We catch the exception here to not populate the error to the toplevel */ - () - }; + ); + + let error = getByRole("error", container); + expect(error->innerHTML)->toBe("An error occured"); + }) { + | _error => + /* We catch the exception here to not populate the error to the toplevel */ + () + }; + }); test( "Memo and normal components rendering with equal and different props", () => { - let container = getContainer(container); - let root = ReactDOM.Client.createRoot(container); - module Normal = { let renders = ref(0); @@ -392,27 +284,28 @@ describe("React", () => { }); }; - act(() => { - ReactDOM.Client.render( - root, + let container = + ReactTestingLibrary.render(
, - ) - }); - act(() => { - ReactDOM.Client.render( - root, -
, - ) - }); - act(() => { - ReactDOM.Client.render( - root, -
, - ) - }); + ); - expect(Normal.renders^)->toBe(3); + expect(Normal.renders^)->toBe(1); + expect(Memo.renders^)->toBe(1); + ReactTestingLibrary.rerender( +
, + container, + ); + + expect(Normal.renders^)->toBe(2); + expect(Memo.renders^)->toBe(1); + + ReactTestingLibrary.rerender( +
, + container, + ); + + expect(Normal.renders^)->toBe(3); expect(Memo.renders^)->toBe(2); }); }); From 33ad56a9277ff387084a9628b5fb9b9df98b9611 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 20:51:28 +0100 Subject: [PATCH 14/26] Force install since we are dealing with R19 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 42d419313..7a88146d6 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ format-check: ## Checks if format is correct .PHONY: install install: ## Update the package dependencies when new deps are added to dune-project @opam install . --deps-only --with-test - @npm install + @npm install --force .PHONY: init create-switch: ## Create a local opam switch From 30d82df3175cdae1d63e0c53a9abc526387f4092 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Tue, 19 Nov 2024 20:59:14 +0100 Subject: [PATCH 15/26] Snapshot with lower {} --- ppx/test/lower.t/run.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ppx/test/lower.t/run.t b/ppx/test/lower.t/run.t index 0898fb7b4..808a6a190 100644 --- a/ppx/test/lower.t/run.t +++ b/ppx/test/lower.t/run.t @@ -94,7 +94,7 @@ ([@merlin.hide] ReactDOM.domProps)( ~children= examples - |> List.map(e => + |> List.map(e => { let Key = e.path; ReactDOM.jsxKeyed( ~key=Key, @@ -120,7 +120,7 @@ ), (), ); - ) + }) |> React.list, (), ), From 507b0fbc0f85e674e07dddd4c1055b50d469f715 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Wed, 20 Nov 2024 12:00:26 +0100 Subject: [PATCH 16/26] Enable ref in ppx --- ppx/reason_react_ppx.ml | 5 ----- ppx/test/component.t/input.re | 7 +++++++ ppx/test/component.t/run.t | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/ppx/reason_react_ppx.ml b/ppx/reason_react_ppx.ml index 9646da10c..b66fa424a 100644 --- a/ppx/reason_react_ppx.ml +++ b/ppx/reason_react_ppx.ml @@ -653,11 +653,6 @@ let jsxMapper = (Invalid_argument "Key cannot be accessed inside of a component. Don't worry - you \ can always key a component from its parent!") - | Pexp_fun (Labelled "ref", _, _, _) | Pexp_fun (Optional "ref", _, _, _) -> - raise - (Invalid_argument - "Ref cannot be passed as a normal prop. Please use `forwardRef` \ - API instead.") | Pexp_fun ( ((Optional label | Labelled label) as arg), default, diff --git a/ppx/test/component.t/input.re b/ppx/test/component.t/input.re index 7fdccd901..3c9839e68 100644 --- a/ppx/test/component.t/input.re +++ b/ppx/test/component.t/input.re @@ -41,6 +41,13 @@ module Forward_Ref = { }); }; +module Ref_as_prop = { + [@react.component] + let make = (~children, ~ref) => { + ; + }; +}; + module Onclick_handler_button = { [@react.component] let make = (~name, ~isDisabled=?) => { diff --git a/ppx/test/component.t/run.t b/ppx/test/component.t/run.t index aa9583ce1..22e4bcbf5 100644 --- a/ppx/test/component.t/run.t +++ b/ppx/test/component.t/run.t @@ -68,6 +68,27 @@ We need to output ML syntax here, otherwise refmt could not parse it. make ~buttonRef:(Props ## buttonRef) ~children:(Props ## children) in Output$Forward_Ref) end + module Ref_as_prop = + struct + external makeProps : + children:'children -> + ref:'ref -> + ?key:string -> unit -> < children: 'children ;ref: 'ref > Js.t + = ""[@@mel.obj ] + let make = + ((fun ~children -> + ((fun ~ref -> + ReactDOM.jsx "button" + (((ReactDOM.domProps)[@merlin.hide ]) ~children ~ref + ~className:"FancyButton" ())) + [@warning "-16"])) + [@warning "-16"]) + let make = + let Output$Ref_as_prop + (Props : < children: 'children ;ref: 'ref > Js.t) = + make ~ref:(Props ## ref) ~children:(Props ## children) in + Output$Ref_as_prop + end module Onclick_handler_button = struct external makeProps : From f3dc9f38fbfd2d22b27c29215d19d08fc63702bd Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Wed, 20 Nov 2024 12:09:57 +0100 Subject: [PATCH 17/26] Add jest test for ref --- test/Ref__test.re | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/Ref__test.re diff --git a/test/Ref__test.re b/test/Ref__test.re new file mode 100644 index 000000000..9dca08716 --- /dev/null +++ b/test/Ref__test.re @@ -0,0 +1,29 @@ +open Jest; +open Expect; + +module Button_with_ref = { + [@react.component] + let make = (~children, ~ref) => { + ; + }; +}; + +let getByRole = (role, container) => { + ReactTestingLibrary.getByRole(~matcher=`Str(role), container); +}; + +[@mel.get] external innerHTML: Dom.element => string = "innerHTML"; + +describe("ref", () => { + test("can be passed as prop to a component", () => { + let domRef = React.createRef(); + let container = + ReactTestingLibrary.render( + + {React.string("Click me")} + , + ); + let button = getByRole("FancyButton", container); + expect(button->innerHTML)->toBe("Click me"); + }) +}); From 80e85aeb712aecbb655a67ebd5a0fc839e2f26d6 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Wed, 20 Nov 2024 17:13:21 +0100 Subject: [PATCH 18/26] Remove jest from demo/dune --- demo/dune | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/dune b/demo/dune index 3f1c1ac86..851bbbc10 100644 --- a/demo/dune +++ b/demo/dune @@ -3,7 +3,7 @@ (alias melange-app) (module_systems (es6 mjs)) - (libraries reason-react jest melange.belt melange.dom) + (libraries reason-react melange.belt melange.dom) (runtime_deps index.html) (preprocess (pps melange.ppx reason-react-ppx))) From b39168fc314e4e1fcfa7d6f699d2bc5202f4288b Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Wed, 20 Nov 2024 17:13:32 +0100 Subject: [PATCH 19/26] Add comment on install --force --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 7a88146d6..d5a930595 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,9 @@ format-check: ## Checks if format is correct .PHONY: install install: ## Update the package dependencies when new deps are added to dune-project @opam install . --deps-only --with-test +# --force is needed because we are installing react@19 while other dependencies +# require react@18. It's a good workaround to bypass the npm validation error +# and test the rc versions of React @npm install --force .PHONY: init From 0470e59f04725a253a036e0dd656185443d6f552 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Wed, 20 Nov 2024 20:09:40 +0100 Subject: [PATCH 20/26] Improve test from checking ref --- test/Ref__test.re | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/Ref__test.re b/test/Ref__test.re index 9dca08716..223a5f348 100644 --- a/test/Ref__test.re +++ b/test/Ref__test.re @@ -25,5 +25,11 @@ describe("ref", () => { ); let button = getByRole("FancyButton", container); expect(button->innerHTML)->toBe("Click me"); + let content = + switch (Js.Nullable.toOption(domRef.current)) { + | Some(element) => element->innerHTML + | None => failwith("No element found") + }; + expect(content)->toBe("Click me"); }) }); From 5171ac584367aa6c4fb5c829b963873913ff5e49 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Mon, 25 Nov 2024 16:09:24 +0100 Subject: [PATCH 21/26] Bind React.act and React.actAsync --- src/React.re | 4 ++++ src/React.rei | 4 ++++ test/React__test.re | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/src/React.re b/src/React.re index 431b7a05a..22ff09744 100644 --- a/src/React.re +++ b/src/React.re @@ -885,6 +885,10 @@ external startTransition: ([@mel.uncurry] (unit => unit)) => unit = external useDebugValue: ('value, ~format: 'value => string=?, unit) => unit = "useDebugValue"; +[@mel.module "react"] external act: (unit => unit) => unit = "act"; +[@mel.module "react"] +external actAsync: (unit => Js.Promise.t(unit)) => unit = "act"; + module Experimental = { /* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */ /* https://react.dev/reference/react/use */ diff --git a/src/React.rei b/src/React.rei index 372337618..1c3be073e 100644 --- a/src/React.rei +++ b/src/React.rei @@ -573,6 +573,10 @@ external startTransition: ([@mel.uncurry] (unit => unit)) => unit = external useTransition: unit => (bool, callback(callback(unit, unit), unit)) = "useTransition"; +[@mel.module "react"] external act: (unit => unit) => unit = "act"; +[@mel.module "react"] +external actAsync: (unit => Js.Promise.t(unit)) => unit = "act"; + module Experimental: { /* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */ [@mel.module "react"] external usePromise: Js.Promise.t('a) => 'a = "use"; diff --git a/test/React__test.re b/test/React__test.re index e7b624355..fa447654b 100644 --- a/test/React__test.re +++ b/test/React__test.re @@ -233,6 +233,38 @@ describe("React", () => { expect(image->getAttribute("src"))->toEqual(Some("https://foo.png")); }); + test("React.act", () => { + module Counter = { + [@react.component] + let make = () => { + let (count, setCount) = React.useState(() => 0); + +
+ + {React.string(string_of_int(count))} +
; + }; + }; + + let containerRef: ref(Js.nullable(ReactTestingLibrary.renderResult)) = + ref(Js.Nullable.null); + + React.act(() => { + let container = ReactTestingLibrary.render(); + let button = getByRole("Increment", container); + FireEvent.click(button); + containerRef.contents = Js.Nullable.return(container); + }); + + switch (Js.Nullable.toOption(containerRef.contents)) { + | Some(container) => + expect(getByRole("counter", container)->innerHTML)->toBe("1") + | None => failwith("Container is null") + }; + }); + test("ErrorBoundary + Suspense", () => { [%mel.raw "console.error = () => {}"] |> ignore; From ad36fbb681b0fe62595fc3acf7f32f1abde1819d Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Mon, 25 Nov 2024 16:09:24 +0100 Subject: [PATCH 22/26] Bind React.act and React.actAsync --- src/React.re | 4 ++++ src/React.rei | 4 ++++ test/Form__test.re | 2 +- test/React__test.re | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/React.re b/src/React.re index 431b7a05a..22ff09744 100644 --- a/src/React.re +++ b/src/React.re @@ -885,6 +885,10 @@ external startTransition: ([@mel.uncurry] (unit => unit)) => unit = external useDebugValue: ('value, ~format: 'value => string=?, unit) => unit = "useDebugValue"; +[@mel.module "react"] external act: (unit => unit) => unit = "act"; +[@mel.module "react"] +external actAsync: (unit => Js.Promise.t(unit)) => unit = "act"; + module Experimental = { /* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */ /* https://react.dev/reference/react/use */ diff --git a/src/React.rei b/src/React.rei index 372337618..1c3be073e 100644 --- a/src/React.rei +++ b/src/React.rei @@ -573,6 +573,10 @@ external startTransition: ([@mel.uncurry] (unit => unit)) => unit = external useTransition: unit => (bool, callback(callback(unit, unit), unit)) = "useTransition"; +[@mel.module "react"] external act: (unit => unit) => unit = "act"; +[@mel.module "react"] +external actAsync: (unit => Js.Promise.t(unit)) => unit = "act"; + module Experimental: { /* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */ [@mel.module "react"] external usePromise: Js.Promise.t('a) => 'a = "use"; diff --git a/test/Form__test.re b/test/Form__test.re index ce34b2922..2269a9c0e 100644 --- a/test/Form__test.re +++ b/test/Form__test.re @@ -87,7 +87,7 @@ module App = { React.useState(() => [ { - text: "¡Hola!", + text: {j|¡Hola!|j}, sending: false, key: 1, }, diff --git a/test/React__test.re b/test/React__test.re index e7b624355..fa447654b 100644 --- a/test/React__test.re +++ b/test/React__test.re @@ -233,6 +233,38 @@ describe("React", () => { expect(image->getAttribute("src"))->toEqual(Some("https://foo.png")); }); + test("React.act", () => { + module Counter = { + [@react.component] + let make = () => { + let (count, setCount) = React.useState(() => 0); + +
+ + {React.string(string_of_int(count))} +
; + }; + }; + + let containerRef: ref(Js.nullable(ReactTestingLibrary.renderResult)) = + ref(Js.Nullable.null); + + React.act(() => { + let container = ReactTestingLibrary.render(); + let button = getByRole("Increment", container); + FireEvent.click(button); + containerRef.contents = Js.Nullable.return(container); + }); + + switch (Js.Nullable.toOption(containerRef.contents)) { + | Some(container) => + expect(getByRole("counter", container)->innerHTML)->toBe("1") + | None => failwith("Container is null") + }; + }); + test("ErrorBoundary + Suspense", () => { [%mel.raw "console.error = () => {}"] |> ignore; From 2f5ae8d3114422db8e9a32b9c10ecc7f0a255b3d Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Mon, 25 Nov 2024 16:46:42 +0100 Subject: [PATCH 23/26] Use React.act as async version only --- src/React.re | 3 +-- src/React.rei | 3 +-- test/React__test.re | 9 +++++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/React.re b/src/React.re index 22ff09744..b5b3b42b6 100644 --- a/src/React.re +++ b/src/React.re @@ -885,9 +885,8 @@ external startTransition: ([@mel.uncurry] (unit => unit)) => unit = external useDebugValue: ('value, ~format: 'value => string=?, unit) => unit = "useDebugValue"; -[@mel.module "react"] external act: (unit => unit) => unit = "act"; [@mel.module "react"] -external actAsync: (unit => Js.Promise.t(unit)) => unit = "act"; +external act: (unit => Js.Promise.t(unit)) => unit = "act"; module Experimental = { /* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */ diff --git a/src/React.rei b/src/React.rei index 1c3be073e..c51cf5f3b 100644 --- a/src/React.rei +++ b/src/React.rei @@ -573,9 +573,8 @@ external startTransition: ([@mel.uncurry] (unit => unit)) => unit = external useTransition: unit => (bool, callback(callback(unit, unit), unit)) = "useTransition"; -[@mel.module "react"] external act: (unit => unit) => unit = "act"; [@mel.module "react"] -external actAsync: (unit => Js.Promise.t(unit)) => unit = "act"; +external act: (unit => Js.Promise.t(unit)) => unit = "act"; module Experimental: { /* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */ diff --git a/test/React__test.re b/test/React__test.re index fa447654b..7c193166f 100644 --- a/test/React__test.re +++ b/test/React__test.re @@ -248,17 +248,18 @@ describe("React", () => { }; }; - let containerRef: ref(Js.nullable(ReactTestingLibrary.renderResult)) = - ref(Js.Nullable.null); + let containerRef: ref(option(ReactTestingLibrary.renderResult)) = + ref(None); React.act(() => { let container = ReactTestingLibrary.render(); let button = getByRole("Increment", container); FireEvent.click(button); - containerRef.contents = Js.Nullable.return(container); + containerRef.contents = Some(container); + Js.Promise.resolve(); }); - switch (Js.Nullable.toOption(containerRef.contents)) { + switch (containerRef.contents) { | Some(container) => expect(getByRole("counter", container)->innerHTML)->toBe("1") | None => failwith("Container is null") From 44facccc90e031e3a2f4113b667079caf0604740 Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Mon, 25 Nov 2024 17:36:40 +0100 Subject: [PATCH 24/26] Test react.act and react.actasync --- src/React.re | 5 +- src/React.rei | 5 +- test/React__test.re | 158 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 138 insertions(+), 30 deletions(-) diff --git a/src/React.re b/src/React.re index b5b3b42b6..ae8cd326e 100644 --- a/src/React.re +++ b/src/React.re @@ -886,7 +886,10 @@ external useDebugValue: ('value, ~format: 'value => string=?, unit) => unit = "useDebugValue"; [@mel.module "react"] -external act: (unit => Js.Promise.t(unit)) => unit = "act"; +external act: (unit => unit) => Js.Promise.t(unit) = "act"; +[@mel.module "react"] +external actAsync: (unit => Js.Promise.t(unit)) => Js.Promise.t(unit) = + "act"; module Experimental = { /* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */ diff --git a/src/React.rei b/src/React.rei index c51cf5f3b..66b4de0ee 100644 --- a/src/React.rei +++ b/src/React.rei @@ -574,7 +574,10 @@ external useTransition: unit => (bool, callback(callback(unit, unit), unit)) = "useTransition"; [@mel.module "react"] -external act: (unit => Js.Promise.t(unit)) => unit = "act"; +external act: (unit => unit) => Js.Promise.t(unit) = "act"; +[@mel.module "react"] +external actAsync: (unit => Js.Promise.t(unit)) => Js.Promise.t(unit) = + "act"; module Experimental: { /* This module is used to bind to APIs for future versions of React. There is no guarantee of backwards compatibility or stability. */ diff --git a/test/React__test.re b/test/React__test.re index 7c193166f..184716112 100644 --- a/test/React__test.re +++ b/test/React__test.re @@ -47,6 +47,7 @@ module DummyContext = { [@mel.get] external tagName: Dom.element => string = "tagName"; [@mel.get] external innerHTML: Dom.element => string = "innerHTML"; +[@mel.set] external setInnerHTML: (Dom.element, string) => unit = "innerHTML"; let getByRole = (role, container) => { ReactTestingLibrary.getByRole(~matcher=`Str(role), container); @@ -62,6 +63,24 @@ let getByTag = (tag, container) => { [@mel.send] external getAttribute: (Dom.element, string) => option(string) = "getAttribute"; +[@mel.set] external setTitle: (Dom.element, string) => unit = "title"; +[@mel.get] external getTitle: Dom.element => string = "title"; + +let (let.await) = (p, f) => Js.Promise.then_(f, p); + +external createElement: string => Dom.element = "document.createElement"; +[@mel.send] +external appendChild: (Dom.element, Dom.element) => unit = "appendChild"; +external document: Dom.element = "document"; +external body: Dom.element = "document.body"; +external querySelector: (string, Dom.element) => option(Dom.element) = + "document.querySelector"; + +[@mel.new] +external mouseEvent: (string, Js.t('a)) => Dom.event = "MouseEvent"; + +[@mel.send] +external dispatchEvent: (Dom.element, Dom.event) => unit = "dispatchEvent"; describe("React", () => { test("can render DOM elements", () => { @@ -233,37 +252,120 @@ describe("React", () => { expect(image->getAttribute("src"))->toEqual(Some("https://foo.png")); }); - test("React.act", () => { - module Counter = { - [@react.component] - let make = () => { - let (count, setCount) = React.useState(() => 0); - -
- - {React.string(string_of_int(count))} -
; - }; + module Counter = { + [@react.component] + let make = () => { + let (count, setCount) = React.Uncurried.useState(() => 0); + + React.useEffect1( + () => { + document->setTitle( + "You clicked " ++ Int.to_string(count) ++ " times", + ); + None; + }, + [|count|], + ); + +
+ + {React.string(string_of_int(count))} +
; }; + }; - let containerRef: ref(option(ReactTestingLibrary.renderResult)) = - ref(None); - - React.act(() => { - let container = ReactTestingLibrary.render(); - let button = getByRole("Increment", container); - FireEvent.click(button); - containerRef.contents = Some(container); - Js.Promise.resolve(); - }); - - switch (containerRef.contents) { - | Some(container) => - expect(getByRole("counter", container)->innerHTML)->toBe("1") - | None => failwith("Container is null") + testPromise("act", finish => { + /* This test doesn't use ReactTestingLibrary to test the act API, and the code comes from + https://react.dev/reference/react/act example */ + + let container: Dom.element = createElement("div"); + body->appendChild(container); + + let.await () = + React.act(() => { + let root = ReactDOM.Client.createRoot(container); + ReactDOM.Client.render(root, ); + }); + + let valueElement = querySelector(".Value", container); + switch (valueElement) { + | Some(value) => expect(value->innerHTML)->toBe("0") + | None => failwith("Can't find 'Value' element") + }; + + let title = getTitle(document); + expect(title)->toBe("You clicked 0 times"); + + let.await () = + React.act(() => { + let buttonElement = querySelector(".Increment", container); + switch (buttonElement) { + | Some(button) => + dispatchEvent(button, mouseEvent("click", {"bubbles": true})) + | None => failwith("Can't find 'Increment' button") + }; + }); + + let valueElement = querySelector(".Value", container); + switch (valueElement) { + | Some(value) => expect(value->innerHTML)->toBe("1") + | None => failwith("Can't find 'Value' element") + }; + + let title = getTitle(document); + expect(title)->toBe("You clicked 1 times"); + + finish(); + }); + + testPromise("actAsync", finish => { + /* This test doesn't use ReactTestingLibrary to test the act API, and the code comes from + https://react.dev/reference/react/act example */ + + body->setInnerHTML(""); + let container: Dom.element = createElement("div"); + body->appendChild(container); + + let.await () = + React.actAsync(() => { + let root = ReactDOM.Client.createRoot(container); + ReactDOM.Client.render(root, ); + Js.Promise.resolve(); + }); + + let valueElement = querySelector(".Value", container); + switch (valueElement) { + | Some(value) => expect(value->innerHTML)->toBe("0") + | None => failwith("Can't find 'Value' element") }; + + let title = getTitle(document); + expect(title)->toBe("You clicked 0 times"); + + let.await () = + React.actAsync(() => { + let buttonElement = querySelector(".Increment", container); + switch (buttonElement) { + | Some(button) => + dispatchEvent(button, mouseEvent("click", {"bubbles": true})) + | None => failwith("Can't find 'Increment' button") + }; + Js.Promise.resolve(); + }); + + let valueElement = querySelector(".Value", container); + switch (valueElement) { + | Some(value) => expect(value->innerHTML)->toBe("1") + | None => failwith("Can't find 'Value' element") + }; + + let title = getTitle(document); + expect(title)->toBe("You clicked 1 times"); + + finish(); }); test("ErrorBoundary + Suspense", () => { From d168359e2d0a593bafba6107c58e7856e424bd1b Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Mon, 25 Nov 2024 17:36:45 +0100 Subject: [PATCH 25/26] Fix hola test :( --- test/Form__test.re | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Form__test.re b/test/Form__test.re index 2269a9c0e..dbbe7eee2 100644 --- a/test/Form__test.re +++ b/test/Form__test.re @@ -136,7 +136,7 @@ describe("Form with useOptimistic", () => { let container = ReactTestingLibrary.render(); ReactTestingLibrary.actAsync(() => { - let.await _ = findByString("Hola!", container); + let.await _ = findByString({j|¡Hola!|j}, container); let.await button = findByString("Enviar", container); let.await input = findByPlaceholderText("message", container); From e12fe314ece8f30b36a583cdb6ff88fedadcdfde Mon Sep 17 00:00:00 2001 From: David Sancho Moreno Date: Mon, 25 Nov 2024 18:33:57 +0100 Subject: [PATCH 26/26] Implement ref with cleanup --- src/ReactDOM.re | 4 ++++ src/ReactDOM.rei | 3 +++ test/Ref__test.re | 51 ++++++++++++++++++++++++++++++++++++++++++++- test/jest/Expect.re | 6 ++++++ test/jest/Mock.re | 2 +- 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/ReactDOM.re b/src/ReactDOM.re index bedb622f2..01d852bf7 100644 --- a/src/ReactDOM.re +++ b/src/ReactDOM.re @@ -487,9 +487,13 @@ module Ref = { type t = domRef; type currentDomRef = React.ref(Js.nullable(Dom.element)); type callbackDomRef = Js.nullable(Dom.element) => unit; + type callbackRefWithCleanup = (Js.nullable(Dom.element), unit) => unit; external domRef: currentDomRef => domRef = "%identity"; external callbackDomRef: callbackDomRef => domRef = "%identity"; + external callbackRefWithCleanup: + [@mel.unwrap] (callbackRefWithCleanup => domRef) = + "%identity"; }; /* This list isn't exhaustive. We'll add more as we go. */ diff --git a/src/ReactDOM.rei b/src/ReactDOM.rei index 9a7eee88c..d8212604c 100644 --- a/src/ReactDOM.rei +++ b/src/ReactDOM.rei @@ -488,9 +488,12 @@ module Ref: { type t = domRef; type currentDomRef = React.ref(Js.nullable(Dom.element)); type callbackDomRef = Js.nullable(Dom.element) => unit; + type callbackRefWithCleanup = (Js.nullable(Dom.element), unit) => unit; external domRef: currentDomRef => domRef = "%identity"; external callbackDomRef: callbackDomRef => domRef = "%identity"; + external callbackRefWithCleanup: callbackRefWithCleanup => domRef = + "%identity"; }; /* This list isn't exhaustive. We'll add more as we go. */ diff --git a/test/Ref__test.re b/test/Ref__test.re index 223a5f348..484aa049a 100644 --- a/test/Ref__test.re +++ b/test/Ref__test.re @@ -31,5 +31,54 @@ describe("ref", () => { | None => failwith("No element found") }; expect(content)->toBe("Click me"); - }) + }); + + test("with callback ref", () => { + let callbackRef = Mock.fn(); + + let refWithCleanup = + ReactDOM.Ref.callbackDomRef(_ref => {ignore(callbackRef())}); + + let _container = + ReactTestingLibrary.render( + + {React.string("Click me")} + , + ); + + expect(callbackRef->Mock.getMock)->toHaveBeenCalled(); + }); + + let (let.await) = (p, f) => Js.Promise.then_(f, p); + + testPromise("with callback ref and cleanup", finish => { + let callbackRef = Mock.fnWithImplementation(_ => ()); + let callbackCleanupRef = Mock.fnWithImplementation(_ => ()); + + let refWithCleanup = + ReactDOM.Ref.callbackRefWithCleanup(_ref => { + callbackRef(); + () => { + callbackCleanupRef(); + }; + }); + + let.await _ = + ReactTestingLibrary.actAsync(() => { + let _container = + ReactTestingLibrary.render( + + {React.string("Click me")} + , + ); + Js.Promise.resolve(); + }); + + expect(callbackRef->Mock.getMock)->toHaveBeenCalledTimes(1); + expect(callbackCleanupRef->Mock.getMock)->toHaveBeenCalledTimes(0); + + ReactTestingLibrary.cleanup(); + expect(callbackCleanupRef->Mock.getMock)->toHaveBeenCalledTimes(1); + finish(); + }); }); diff --git a/test/jest/Expect.re b/test/jest/Expect.re index 0b1e0a6a0..bd0f1e37c 100644 --- a/test/jest/Expect.re +++ b/test/jest/Expect.re @@ -20,6 +20,12 @@ external toBeLessThanOrEqual: (t('a), 'a) => unit = "toBeLessThanOrEqual"; [@mel.send] external toHaveLength: (t(array('a)), 'a) => unit = "toHaveLength"; +[@mel.send] +external toHaveBeenCalled: (t('a), unit) => unit = "toHaveBeenCalled"; +[@mel.send] +external toHaveBeenCalledTimes: (t('a), int) => unit = + "toHaveBeenCalledTimes"; + [@mel.get] external rejects: t(Js.Promise.t('a)) => t(unit => 'a) = "rejects"; diff --git a/test/jest/Mock.re b/test/jest/Mock.re index 2301b7564..04401920f 100644 --- a/test/jest/Mock.re +++ b/test/jest/Mock.re @@ -5,7 +5,7 @@ let undefined: undefined = Js.Undefined.empty; type mock('a); type t('a) = mock('a); -[@mel.scope "jest"] external fn: unit => 'a = "fn"; +[@mel.scope "jest"] external fn: [@mel.unwrap] (unit => 'a) = "fn"; [@mel.scope "jest"] external fnWithImplementation: 'a => 'a = "fn"; [@mel.scope "jest"] external mockModule: string => unit = "mock"; external getMock: 'a => t('a) = "%identity";