From 9b88a70fe4d8497fa256f0e552a0e7f662b75d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Vidra?= Date: Tue, 21 Jan 2025 11:18:41 +0100 Subject: [PATCH] test(react): share js tests --- pnpm-lock.yaml | 471 +++++++++++++++++- workspaces/e2e/package.json | 13 +- workspaces/e2e/{tests => pages}/index.html | 0 .../{tests/js/index.html => pages/js.html} | 4 +- .../e2e/{tests/js/script.ts => pages/js.ts} | 0 .../e2e/{tests => pages}/js/block/block.html | 0 .../e2e/{tests => pages}/js/block/block.ts | 0 workspaces/e2e/pages/react.html | 12 + workspaces/e2e/pages/react.tsx | 44 ++ workspaces/e2e/{tests => pages}/reset.css | 0 workspaces/e2e/tests/events.spec.ts | 63 +++ workspaces/e2e/tests/init.spec.ts | 52 ++ .../e2e/tests/js/{block => }/block.spec.ts | 0 workspaces/e2e/tests/js/events.spec.ts | 58 --- workspaces/e2e/tests/js/init.spec.ts | 45 -- .../e2e/tests/js/page-targeting.spec.ts | 174 ------- workspaces/e2e/tests/js/slot.spec.ts | 82 --- workspaces/e2e/tests/js/tour.spec.ts | 116 ----- workspaces/e2e/tests/js/wait.spec.ts | 182 ------- workspaces/e2e/tests/js/websocket.spec.ts | 48 -- workspaces/e2e/tests/page-targeting.spec.ts | 193 +++++++ workspaces/e2e/tests/slot.spec.ts | 82 +++ workspaces/e2e/tests/tour.spec.ts | 121 +++++ workspaces/e2e/tests/wait.spec.ts | 191 +++++++ workspaces/e2e/tests/websocket.spec.ts | 57 +++ workspaces/e2e/tsconfig.json | 5 +- workspaces/e2e/vite.config.js | 7 +- workspaces/react-components/package.json | 3 +- 28 files changed, 1286 insertions(+), 737 deletions(-) rename workspaces/e2e/{tests => pages}/index.html (100%) rename workspaces/e2e/{tests/js/index.html => pages/js.html} (77%) rename workspaces/e2e/{tests/js/script.ts => pages/js.ts} (100%) rename workspaces/e2e/{tests => pages}/js/block/block.html (100%) rename workspaces/e2e/{tests => pages}/js/block/block.ts (100%) create mode 100644 workspaces/e2e/pages/react.html create mode 100644 workspaces/e2e/pages/react.tsx rename workspaces/e2e/{tests => pages}/reset.css (100%) create mode 100644 workspaces/e2e/tests/events.spec.ts create mode 100644 workspaces/e2e/tests/init.spec.ts rename workspaces/e2e/tests/js/{block => }/block.spec.ts (100%) delete mode 100644 workspaces/e2e/tests/js/events.spec.ts delete mode 100644 workspaces/e2e/tests/js/init.spec.ts delete mode 100644 workspaces/e2e/tests/js/page-targeting.spec.ts delete mode 100644 workspaces/e2e/tests/js/slot.spec.ts delete mode 100644 workspaces/e2e/tests/js/tour.spec.ts delete mode 100644 workspaces/e2e/tests/js/wait.spec.ts delete mode 100644 workspaces/e2e/tests/js/websocket.spec.ts create mode 100644 workspaces/e2e/tests/page-targeting.spec.ts create mode 100644 workspaces/e2e/tests/slot.spec.ts create mode 100644 workspaces/e2e/tests/tour.spec.ts create mode 100644 workspaces/e2e/tests/wait.spec.ts create mode 100644 workspaces/e2e/tests/websocket.spec.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91173a23..01195c21 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,12 @@ importers: '@flows/js-components': specifier: workspace:* version: link:../js-components + '@flows/react': + specifier: workspace:* + version: link:../react + '@flows/react-components': + specifier: workspace:* + version: link:../react-components '@flows/shared': specifier: workspace:* version: link:../shared @@ -54,12 +60,27 @@ importers: '@types/node': specifier: ^20 version: 20.17.14 + '@types/react': + specifier: 19.0.7 + version: 19.0.7 + '@types/react-dom': + specifier: 19.0.3 + version: 19.0.3(@types/react@19.0.7) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.3.4(vite@6.0.9(@types/node@20.17.14)(jiti@2.4.2)(yaml@2.4.5)) + react: + specifier: 19.0.0 + version: 19.0.0 + react-dom: + specifier: 19.0.0 + version: 19.0.0(react@19.0.0) typescript: specifier: ^5.7.3 version: 5.7.3 vite: - specifier: ^6.0.7 - version: 6.0.7(@types/node@20.17.14)(jiti@2.4.2)(yaml@2.4.5) + specifier: ^6.0.9 + version: 6.0.9(@types/node@20.17.14)(jiti@2.4.2)(yaml@2.4.5) workspaces/js: dependencies: @@ -121,7 +142,7 @@ importers: version: 6.0.1 ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.24.0)(jest@29.7.0(@types/node@20.17.14))(typescript@5.7.3) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.14))(typescript@5.7.3) tsup: specifier: ^8.3.5 version: 8.3.5(@microsoft/api-extractor@7.48.1(@types/node@20.17.14))(jiti@2.4.2)(postcss@8.5.1)(typescript@5.7.3)(yaml@2.4.5) @@ -155,7 +176,7 @@ importers: version: 29.7.0 ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.24.0)(jest@29.7.0(@types/node@20.17.14))(typescript@5.7.3) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.14))(typescript@5.7.3) tsup: specifier: ^8.3.5 version: 8.3.5(@microsoft/api-extractor@7.48.1(@types/node@20.17.14))(jiti@2.4.2)(postcss@8.5.1)(typescript@5.7.3)(yaml@2.4.5) @@ -167,7 +188,7 @@ importers: dependencies: '@floating-ui/react-dom': specifier: ^2.1.2 - version: 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 2.1.2(react-dom@19.0.0(react@18.3.1))(react@18.3.1) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -198,7 +219,7 @@ importers: version: 29.7.0 ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.24.0)(jest@29.7.0(@types/node@20.17.14))(typescript@5.7.3) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.14))(typescript@5.7.3) tsup: specifier: ^8.3.5 version: 8.3.5(@microsoft/api-extractor@7.48.1(@types/node@20.17.14))(jiti@2.4.2)(postcss@8.5.1)(typescript@5.7.3)(yaml@2.4.5) @@ -219,7 +240,7 @@ importers: version: 29.7.0(@types/node@20.17.14) ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.24.0)(jest@29.7.0(@types/node@20.17.14))(typescript@5.7.3) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.14))(typescript@5.7.3) typescript: specifier: ^5.7.3 version: 5.7.3 @@ -236,14 +257,26 @@ packages: resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.24.7': resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.26.5': + resolution: {integrity: sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==} + engines: {node: '>=6.9.0'} + '@babel/core@7.24.7': resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} engines: {node: '>=6.9.0'} + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + engines: {node: '>=6.9.0'} + '@babel/eslint-parser@7.24.7': resolution: {integrity: sha512-SO5E3bVxDuxyNxM5agFv480YA2HO6ohZbGxbazZdIk3KQOPOGVNw6q78I9/lbviIf95eq6tPozeYnJLbjnC8IA==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} @@ -255,10 +288,18 @@ packages: resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} engines: {node: '>=6.9.0'} + '@babel/generator@7.26.5': + resolution: {integrity: sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==} + engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.24.7': resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.26.5': + resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} + engines: {node: '>=6.9.0'} + '@babel/helper-environment-visitor@7.24.7': resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} engines: {node: '>=6.9.0'} @@ -275,16 +316,30 @@ packages: resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.24.7': resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-plugin-utils@7.24.7': resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.26.5': + resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} + engines: {node: '>=6.9.0'} + '@babel/helper-simple-access@7.24.7': resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} engines: {node: '>=6.9.0'} @@ -297,18 +352,34 @@ packages: resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.24.7': resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.24.7': resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + '@babel/helpers@7.24.7': resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + engines: {node: '>=6.9.0'} + '@babel/highlight@7.24.7': resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} @@ -318,6 +389,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.26.5': + resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -391,6 +467,18 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-self@7.25.9': + resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.25.9': + resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/runtime@7.24.7': resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} engines: {node: '>=6.9.0'} @@ -399,14 +487,26 @@ packages: resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} engines: {node: '>=6.9.0'} + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.24.7': resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.26.5': + resolution: {integrity: sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==} + engines: {node: '>=6.9.0'} + '@babel/types@7.24.7': resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.26.5': + resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -1248,9 +1348,17 @@ packages: '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/react-dom@19.0.3': + resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==} + peerDependencies: + '@types/react': ^19.0.0 + '@types/react@19.0.2': resolution: {integrity: sha512-USU8ZI/xyKJwFTpjSVIrSeHBVAGagkHQKPNbxeWwql/vDmnTIBgx+TJnhFnj1NXgz8XfprU0egV2dROLGpsBEg==} + '@types/react@19.0.7': + resolution: {integrity: sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==} + '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} @@ -1372,6 +1480,12 @@ packages: typescript: optional: true + '@vitejs/plugin-react@4.3.4': + resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -1578,6 +1692,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} engines: {node: '>= 6'} @@ -1621,6 +1740,9 @@ packages: caniuse-lite@1.0.30001632: resolution: {integrity: sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==} + caniuse-lite@1.0.30001695: + resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1895,6 +2017,9 @@ packages: electron-to-chromium@1.4.798: resolution: {integrity: sha512-by9J2CiM9KPGj9qfp5U4FcPSbXJG7FNzqnYaY4WLzX+v2PHieVGmnsA4dxfpGE3QEC7JofpPZmn7Vn1B9NR2+Q==} + electron-to-chromium@1.5.83: + resolution: {integrity: sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==} + emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -3053,6 +3178,9 @@ packages: node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -3289,10 +3417,10 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + react-dom@19.0.0: + resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} peerDependencies: - react: ^18.3.1 + react: ^19.0.0 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -3300,10 +3428,18 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} + react@19.0.0: + resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} + engines: {node: '>=0.10.0'} + read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -3421,8 +3557,8 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} - scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + scheduler@0.25.0: + resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} @@ -3818,6 +3954,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.1.2: + resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -3831,8 +3973,8 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - vite@6.0.7: - resolution: {integrity: sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==} + vite@6.0.9: + resolution: {integrity: sha512-MSgUxHcaXLtnBPktkbUSoQUANApKYuxZ6DrbVENlIorbhL2dZydTLaZ01tjUoE3szeFzlFk9ANOKk0xurh4MKA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -4001,8 +4143,16 @@ snapshots: '@babel/highlight': 7.24.7 picocolors: 1.1.1 + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.24.7': {} + '@babel/compat-data@7.26.5': {} + '@babel/core@7.24.7': dependencies: '@ampproject/remapping': 2.3.0 @@ -4023,6 +4173,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.26.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.5 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.5 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 + convert-source-map: 2.0.0 + debug: 4.3.7 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/eslint-parser@7.24.7(@babel/core@7.24.7)(eslint@8.57.0)': dependencies: '@babel/core': 7.24.7 @@ -4038,6 +4208,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 + '@babel/generator@7.26.5': + dependencies: + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.0.2 + '@babel/helper-compilation-targets@7.24.7': dependencies: '@babel/compat-data': 7.24.7 @@ -4046,6 +4224,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@7.26.5': + dependencies: + '@babel/compat-data': 7.26.5 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.4 + lru-cache: 5.1.1 + semver: 6.3.1 + '@babel/helper-environment-visitor@7.24.7': dependencies: '@babel/types': 7.24.7 @@ -4066,6 +4252,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 @@ -4077,8 +4270,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.5 + transitivePeerDependencies: + - supports-color + '@babel/helper-plugin-utils@7.24.7': {} + '@babel/helper-plugin-utils@7.26.5': {} + '@babel/helper-simple-access@7.24.7': dependencies: '@babel/traverse': 7.24.7 @@ -4092,15 +4296,26 @@ snapshots: '@babel/helper-string-parser@7.24.7': {} + '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-validator-identifier@7.24.7': {} + '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-option@7.24.7': {} + '@babel/helper-validator-option@7.25.9': {} + '@babel/helpers@7.24.7': dependencies: '@babel/template': 7.24.7 '@babel/types': 7.24.7 + '@babel/helpers@7.26.0': + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.5 + '@babel/highlight@7.24.7': dependencies: '@babel/helper-validator-identifier': 7.24.7 @@ -4112,31 +4327,65 @@ snapshots: dependencies: '@babel/types': 7.24.7 + '@babel/parser@7.26.5': + dependencies: + '@babel/types': 7.26.5 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 @@ -4147,41 +4396,93 @@ snapshots: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.24.7 + optional: true + '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/runtime@7.24.7': dependencies: regenerator-runtime: 0.14.1 @@ -4192,6 +4493,12 @@ snapshots: '@babel/parser': 7.24.7 '@babel/types': 7.24.7 + '@babel/template@7.25.9': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 + '@babel/traverse@7.24.7': dependencies: '@babel/code-frame': 7.24.7 @@ -4207,12 +4514,29 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.26.5': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.5 + '@babel/template': 7.25.9 + '@babel/types': 7.26.5 + debug: 4.3.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + '@babel/types@7.24.7': dependencies: '@babel/helper-string-parser': 7.24.7 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + '@babel/types@7.26.5': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@bcoe/v8-coverage@0.2.3': {} '@commitlint/cli@19.6.1(@types/node@22.10.7)(typescript@5.7.3)': @@ -4509,11 +4833,11 @@ snapshots: '@floating-ui/core': 1.6.2 '@floating-ui/utils': 0.2.9 - '@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/dom': 1.6.12 react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 19.0.0(react@18.3.1) '@floating-ui/utils@0.2.8': {} @@ -5027,10 +5351,18 @@ snapshots: '@types/normalize-package-data@2.4.4': {} + '@types/react-dom@19.0.3(@types/react@19.0.7)': + dependencies: + '@types/react': 19.0.7 + '@types/react@19.0.2': dependencies: csstype: 3.1.3 + '@types/react@19.0.7': + dependencies: + csstype: 3.1.3 + '@types/semver@7.5.8': {} '@types/stack-utils@2.0.3': {} @@ -5200,6 +5532,17 @@ snapshots: - supports-color - vitest + '@vitejs/plugin-react@4.3.4(vite@6.0.9(@types/node@20.17.14)(jiti@2.4.2)(yaml@2.4.5))': + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.2 + vite: 6.0.9(@types/node@20.17.14)(jiti@2.4.2)(yaml@2.4.5) + transitivePeerDependencies: + - supports-color + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -5406,6 +5749,20 @@ snapshots: transitivePeerDependencies: - supports-color + babel-jest@29.7.0(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.26.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + optional: true + babel-plugin-istanbul@6.1.1: dependencies: '@babel/helper-plugin-utils': 7.24.7 @@ -5439,12 +5796,36 @@ snapshots: '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) + babel-preset-current-node-syntax@1.0.1(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.0) + optional: true + babel-preset-jest@29.6.3(@babel/core@7.24.7): dependencies: '@babel/core': 7.24.7 babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) + babel-preset-jest@29.6.3(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.26.0) + optional: true + balanced-match@1.0.2: {} brace-expansion@1.1.11: @@ -5467,6 +5848,13 @@ snapshots: node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.1) + browserslist@4.24.4: + dependencies: + caniuse-lite: 1.0.30001695 + electron-to-chromium: 1.5.83 + node-releases: 2.0.19 + update-browserslist-db: 1.1.2(browserslist@4.24.4) + bs-logger@0.2.6: dependencies: fast-json-stable-stringify: 2.1.0 @@ -5502,6 +5890,8 @@ snapshots: caniuse-lite@1.0.30001632: {} + caniuse-lite@1.0.30001695: {} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -5764,6 +6154,8 @@ snapshots: electron-to-chromium@1.4.798: {} + electron-to-chromium@1.5.83: {} + emittery@0.13.1: {} emoji-regex@8.0.0: {} @@ -7341,6 +7733,8 @@ snapshots: node-releases@2.0.14: {} + node-releases@2.0.19: {} + normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 @@ -7560,20 +7954,28 @@ snapshots: queue-microtask@1.2.3: {} - react-dom@18.3.1(react@18.3.1): + react-dom@19.0.0(react@18.3.1): dependencies: - loose-envify: 1.4.0 react: 18.3.1 - scheduler: 0.23.2 + scheduler: 0.25.0 + + react-dom@19.0.0(react@19.0.0): + dependencies: + react: 19.0.0 + scheduler: 0.25.0 react-is@16.13.1: {} react-is@18.3.1: {} + react-refresh@0.14.2: {} + react@18.3.1: dependencies: loose-envify: 1.4.0 + react@19.0.0: {} + read-pkg-up@7.0.1: dependencies: find-up: 4.1.0 @@ -7736,9 +8138,7 @@ snapshots: dependencies: xmlchars: 2.2.0 - scheduler@0.23.2: - dependencies: - loose-envify: 1.4.0 + scheduler@0.25.0: {} semver@5.7.2: {} @@ -8023,6 +8423,25 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.24.7) esbuild: 0.24.0 + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.14))(typescript@5.7.3): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@20.17.14) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.7.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.26.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -8140,6 +8559,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.1.2(browserslist@4.24.4): + dependencies: + browserslist: 4.24.4 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -8160,7 +8585,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite@6.0.7(@types/node@20.17.14)(jiti@2.4.2)(yaml@2.4.5): + vite@6.0.9(@types/node@20.17.14)(jiti@2.4.2)(yaml@2.4.5): dependencies: esbuild: 0.24.2 postcss: 8.5.1 diff --git a/workspaces/e2e/package.json b/workspaces/e2e/package.json index cb2c74da..0855eab4 100644 --- a/workspaces/e2e/package.json +++ b/workspaces/e2e/package.json @@ -2,22 +2,29 @@ "name": "e2e", "private": true, "scripts": { - "serve:test": "vite", "playwright": "playwright test", - "playwright:install": "playwright install --with-deps", "playwright:docker": "./docker_run_playwright.sh /app/workspaces/e2e/docker_playwright_script.sh", "playwright:docker:update-snapshots": "./docker_run_playwright.sh /app/workspaces/e2e/docker_playwright_script_update_snapshots.sh", + "playwright:install": "playwright install --with-deps", + "serve:test": "vite", "tsc": "tsc -p tsconfig.json" }, "dependencies": { "@flows/js": "workspace:*", "@flows/js-components": "workspace:*", + "@flows/react": "workspace:*", + "@flows/react-components": "workspace:*", "@flows/shared": "workspace:*" }, "devDependencies": { "@playwright/test": "^1.49.1", "@types/node": "^20", + "@types/react": "19.0.7", + "@types/react-dom": "19.0.3", + "@vitejs/plugin-react": "^4.3.4", + "react": "19.0.0", + "react-dom": "19.0.0", "typescript": "^5.7.3", - "vite": "^6.0.7" + "vite": "^6.0.9" } } diff --git a/workspaces/e2e/tests/index.html b/workspaces/e2e/pages/index.html similarity index 100% rename from workspaces/e2e/tests/index.html rename to workspaces/e2e/pages/index.html diff --git a/workspaces/e2e/tests/js/index.html b/workspaces/e2e/pages/js.html similarity index 77% rename from workspaces/e2e/tests/js/index.html rename to workspaces/e2e/pages/js.html index bb31483d..fffd7815 100644 --- a/workspaces/e2e/tests/js/index.html +++ b/workspaces/e2e/pages/js.html @@ -3,7 +3,7 @@ - +

heading 1

@@ -15,6 +15,6 @@

Subtitle

- + diff --git a/workspaces/e2e/tests/js/script.ts b/workspaces/e2e/pages/js.ts similarity index 100% rename from workspaces/e2e/tests/js/script.ts rename to workspaces/e2e/pages/js.ts diff --git a/workspaces/e2e/tests/js/block/block.html b/workspaces/e2e/pages/js/block/block.html similarity index 100% rename from workspaces/e2e/tests/js/block/block.html rename to workspaces/e2e/pages/js/block/block.html diff --git a/workspaces/e2e/tests/js/block/block.ts b/workspaces/e2e/pages/js/block/block.ts similarity index 100% rename from workspaces/e2e/tests/js/block/block.ts rename to workspaces/e2e/pages/js/block/block.ts diff --git a/workspaces/e2e/pages/react.html b/workspaces/e2e/pages/react.html new file mode 100644 index 00000000..daaaecb9 --- /dev/null +++ b/workspaces/e2e/pages/react.html @@ -0,0 +1,12 @@ + + + + + + + + +
+ + + diff --git a/workspaces/e2e/pages/react.tsx b/workspaces/e2e/pages/react.tsx new file mode 100644 index 00000000..a0cc0b13 --- /dev/null +++ b/workspaces/e2e/pages/react.tsx @@ -0,0 +1,44 @@ +import { FlowsProvider, FlowsSlot } from "@flows/react"; +import { FC, StrictMode } from "react"; +import { createRoot } from "react-dom/client"; + +import * as components from "@flows/react-components"; +import * as tourComponents from "@flows/react-components/tour"; +import "@flows/react-components/index.css"; + +const apiUrl = new URLSearchParams(window.location.search).get("apiUrl") ?? undefined; + +const Card: FC<{ text: string }> = (props) => ( +
+

{props.text}

+
+); + +createRoot(document.getElementById("root")!).render( + + +

heading 1

+

Subtitle

+ + Slot placeholder

} /> +
+
, +); diff --git a/workspaces/e2e/tests/reset.css b/workspaces/e2e/pages/reset.css similarity index 100% rename from workspaces/e2e/tests/reset.css rename to workspaces/e2e/pages/reset.css diff --git a/workspaces/e2e/tests/events.spec.ts b/workspaces/e2e/tests/events.spec.ts new file mode 100644 index 00000000..0a4613ee --- /dev/null +++ b/workspaces/e2e/tests/events.spec.ts @@ -0,0 +1,63 @@ +import { Block } from "@flows/shared"; +import { test, expect } from "@playwright/test"; + +test.beforeEach(async ({ page }) => { + await page.routeWebSocket( + (url) => url.pathname === "/ws/sdk/block-updates", + () => {}, + ); +}); + +const block: Block = { + id: "w", + type: "component", + componentType: "Modal", + data: { title: "Workflow block", body: "", continueText: "continue" }, + exitNodes: [], + slottable: false, +}; + +const run = (packageName: string) => { + test(`${packageName} - shouldn't pass any methods without exit nodes`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [block] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Workflow block")).toBeVisible(); + let reqWasSent = false; + page.on("request", (req) => { + if (req.url().includes("v2/sdk/events")) { + reqWasSent = true; + } + }); + await page.getByText("continue").click(); + expect(reqWasSent).toBe(false); + }); + test(`${packageName} - should pass methods with exit nodes`, async ({ page }) => { + const b: Block = { + ...block, + exitNodes: ["continue"], + }; + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [b] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Workflow block")).toBeVisible(); + const req = page.waitForRequest((req) => { + const body = req.postDataJSON(); + return ( + req.url() === "https://api.flows-cloud.com/v2/sdk/events" && + body.organizationId === "orgId" && + body.userId === "testUserId" && + body.environment === "prod" && + body.name === "transition" && + body.propertyKey === "continue" + ); + }); + await page.getByText("continue").click(); + await req; + }); +}; + +run("js"); +run("react"); diff --git a/workspaces/e2e/tests/init.spec.ts b/workspaces/e2e/tests/init.spec.ts new file mode 100644 index 00000000..b49ef450 --- /dev/null +++ b/workspaces/e2e/tests/init.spec.ts @@ -0,0 +1,52 @@ +import { test } from "@playwright/test"; + +test.beforeEach(async ({ page }) => { + await page.routeWebSocket( + (url) => url.pathname === "/ws/sdk/block-updates", + () => {}, + ); +}); + +const run = (packageName: string) => { + test(`${packageName} - should call blocks with correct parameters`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [] } }); + }); + const blocksReq = page.waitForRequest((req) => { + const body = req.postDataJSON(); + return ( + req.url() === "https://api.flows-cloud.com/v2/sdk/blocks" && + body.organizationId === "orgId" && + body.userId === "testUserId" && + body.environment === "prod" && + body.userProperties.email === "test@flows.sh" && + body.userProperties.age === 10 + ); + }); + await page.goto(`/${packageName}.html`); + await blocksReq; + }); + test(`${packageName} - should call custom apiUrl`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [] } }); + }); + const blocksReq = page.waitForRequest((req) => { + const body = req.postDataJSON(); + return ( + req.url() === "https://custom.api.flows.com/v2/sdk/blocks" && + body.organizationId === "orgId" && + body.userId === "testUserId" && + body.environment === "prod" && + body.userProperties.email === "test@flows.sh" && + body.userProperties.age === 10 + ); + }); + await page.goto( + `/${packageName}.html?apiUrl=${encodeURIComponent("https://custom.api.flows.com")}`, + ); + await blocksReq; + }); +}; + +run("js"); +run("react"); diff --git a/workspaces/e2e/tests/js/block/block.spec.ts b/workspaces/e2e/tests/js/block.spec.ts similarity index 100% rename from workspaces/e2e/tests/js/block/block.spec.ts rename to workspaces/e2e/tests/js/block.spec.ts diff --git a/workspaces/e2e/tests/js/events.spec.ts b/workspaces/e2e/tests/js/events.spec.ts deleted file mode 100644 index 01205e7f..00000000 --- a/workspaces/e2e/tests/js/events.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Block } from "@flows/shared"; -import test, { expect } from "@playwright/test"; - -test.beforeEach(async ({ page }) => { - await page.routeWebSocket( - (url) => url.pathname === "/ws/sdk/block-updates", - () => {}, - ); -}); - -const block: Block = { - id: "w", - type: "component", - componentType: "Modal", - data: { title: "Workflow block", body: "", continueText: "continue" }, - exitNodes: [], - slottable: false, -}; - -test("shouldn't pass any methods without exit nodes", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [block] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Workflow block")).toBeVisible(); - let reqWasSent = false; - page.on("request", (req) => { - if (req.url().includes("v2/sdk/events")) { - reqWasSent = true; - } - }); - await page.getByText("continue").click(); - expect(reqWasSent).toBe(false); -}); -test("should pass methods with exit nodes", async ({ page }) => { - const b: Block = { - ...block, - exitNodes: ["continue"], - }; - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [b] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Workflow block")).toBeVisible(); - const req = page.waitForRequest((req) => { - const body = req.postDataJSON(); - return ( - req.url() === "https://api.flows-cloud.com/v2/sdk/events" && - body.organizationId === "orgId" && - body.userId === "testUserId" && - body.environment === "prod" && - body.name === "transition" && - body.propertyKey === "continue" - ); - }); - await page.getByText("continue").click(); - await req; -}); diff --git a/workspaces/e2e/tests/js/init.spec.ts b/workspaces/e2e/tests/js/init.spec.ts deleted file mode 100644 index eed0982c..00000000 --- a/workspaces/e2e/tests/js/init.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { test } from "@playwright/test"; - -test.beforeEach(async ({ page }) => { - await page.routeWebSocket( - (url) => url.pathname === "/ws/sdk/block-updates", - () => {}, - ); -}); - -test("should call blocks with correct parameters", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [] } }); - }); - const blocksReq = page.waitForRequest((req) => { - const body = req.postDataJSON(); - return ( - req.url() === "https://api.flows-cloud.com/v2/sdk/blocks" && - body.organizationId === "orgId" && - body.userId === "testUserId" && - body.environment === "prod" && - body.userProperties.email === "test@flows.sh" && - body.userProperties.age === 10 - ); - }); - await page.goto(`/js/index.html`); - await blocksReq; -}); -test("should call custom apiUrl", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [] } }); - }); - const blocksReq = page.waitForRequest((req) => { - const body = req.postDataJSON(); - return ( - req.url() === "https://custom.api.flows.com/v2/sdk/blocks" && - body.organizationId === "orgId" && - body.userId === "testUserId" && - body.environment === "prod" && - body.userProperties.email === "test@flows.sh" && - body.userProperties.age === 10 - ); - }); - await page.goto(`/js/index.html?apiUrl=${encodeURIComponent("https://custom.api.flows.com")}`); - await blocksReq; -}); diff --git a/workspaces/e2e/tests/js/page-targeting.spec.ts b/workspaces/e2e/tests/js/page-targeting.spec.ts deleted file mode 100644 index 7d4f0110..00000000 --- a/workspaces/e2e/tests/js/page-targeting.spec.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { expect, test } from "@playwright/test"; -import { Block, TourStep } from "@flows/shared"; - -test.beforeEach(async ({ page }) => { - await page.routeWebSocket( - (url) => url.pathname === "/ws/sdk/block-updates", - () => {}, - ); -}); - -const floatingWorkflowBlock: Block = { - id: "w", - type: "component", - componentType: "Modal", - data: { title: "Workflow block", body: "" }, - exitNodes: [], - slottable: false, -}; - -// Floating workflow -test("should show workflow block without page targeting", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [floatingWorkflowBlock] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Workflow block")).toBeVisible(); -}); - -test("should not show workflow block with incorrect page targeting", async ({ page }) => { - const block: Block = { - ...floatingWorkflowBlock, - page_targeting_operator: "contains", - page_targeting_values: ["/wrong"], - }; - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [block] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Workflow block")).toBeHidden(); -}); - -test("should show workflow block with correct page targeting", async ({ page }) => { - const block: Block = { - ...floatingWorkflowBlock, - page_targeting_operator: "contains", - page_targeting_values: ["/js/index.html"], - }; - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [block] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Workflow block")).toBeVisible(); -}); - -const slotBlock: Block = { - id: "w", - type: "component", - componentType: "Card", - data: { text: "slottable block" }, - exitNodes: [], - slottable: true, - slotId: "my-slot", -}; - -// Workflow slot -test("should not show workflow block in slot with incorrect page targeting", async ({ page }) => { - const block: Block = { - ...slotBlock, - page_targeting_operator: "contains", - page_targeting_values: ["/wrong"], - }; - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [block] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("slottable block")).toBeHidden(); -}); -test("should show workflow block in slot with correct page targeting", async ({ page }) => { - const block: Block = { - ...slotBlock, - page_targeting_operator: "contains", - page_targeting_values: ["/js/index.html"], - }; - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [block] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("slottable block")).toBeVisible(); -}); - -const getTour = (steps: TourStep[]): Block => ({ - id: "wt", - type: "tour", - data: {}, - exitNodes: ["complete", "cancel"], - slottable: false, - tourBlocks: steps, -}); - -const floatingTourModalBlock: TourStep = { - id: "t", - type: "tour-component", - componentType: "Modal", - data: { title: "Tour block", body: "" }, - slottable: false, -}; - -// Floating tour -test("should show tour block without page targeting", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [getTour([floatingTourModalBlock])] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Tour block")).toBeVisible(); -}); -test("should not show tour block with incorrect page targeting", async ({ page }) => { - const block: TourStep = { - ...floatingTourModalBlock, - page_targeting_operator: "contains", - page_targeting_values: ["/wrong"], - }; - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [getTour([block])] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Tour block")).toBeHidden(); -}); -test("should show tour block with correct page targeting", async ({ page }) => { - const block: TourStep = { - ...floatingTourModalBlock, - page_targeting_operator: "contains", - page_targeting_values: ["/js/index.html"], - }; - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [getTour([block])] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Tour block")).toBeVisible(); -}); - -const slotTourModalBlock: TourStep = { - id: "t", - type: "tour-component", - componentType: "Card", - data: { text: "slottable tour block" }, - slottable: true, - slotId: "my-slot", -}; - -// Tour slot -test("should not show tour block in slot with incorrect page targeting", async ({ page }) => { - const block: TourStep = { - ...slotTourModalBlock, - page_targeting_operator: "contains", - page_targeting_values: ["/wrong"], - }; - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [getTour([block])] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Tour block")).toBeHidden(); -}); -test("should show tour block in slot with correct page targeting", async ({ page }) => { - const block: TourStep = { - ...slotTourModalBlock, - page_targeting_operator: "contains", - page_targeting_values: ["/js/index.html"], - }; - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [getTour([block])] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Tour block")).toBeVisible(); -}); diff --git a/workspaces/e2e/tests/js/slot.spec.ts b/workspaces/e2e/tests/js/slot.spec.ts deleted file mode 100644 index 3131b20b..00000000 --- a/workspaces/e2e/tests/js/slot.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Block } from "@flows/shared"; -import test, { expect } from "@playwright/test"; - -test.beforeEach(async ({ page }) => { - await page.routeWebSocket( - (url) => url.pathname === "/ws/sdk/block-updates", - () => {}, - ); -}); - -const getCard = (props: { slotIndex?: number; text: string }): Block => ({ - id: "c", - type: "component", - componentType: "Card", - data: { text: props.text }, - exitNodes: [], - slottable: true, - slotId: "my-slot", - slotIndex: props.slotIndex, -}); - -test("should render empty slot", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.locator("flows-slot")).toBeVisible(); - await expect(page.getByText("Slot placeholder")).toBeVisible(); -}); -test("should render block in slot and hide placeholder", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [getCard({ text: "Hello world" })] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.locator("flows-slot")).toBeVisible(); - await expect(page.getByText("Slot placeholder")).toBeHidden(); - await expect(page.getByText("Hello world")).toBeVisible(); - await expect(page.locator(".flows-card")).toBeVisible(); -}); -test("should sort blocks by slotIndex", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ - json: { - blocks: [getCard({ text: "block number one" }), getCard({ text: "block number two" })], - }, - }); - }); - await page.goto(`/js/index.html`); - await expect(page.locator("flows-slot")).toBeVisible(); - await expect(page.locator(".flows-card").nth(0)).toHaveText("block number one"); - await expect(page.locator(".flows-card").nth(1)).toHaveText("block number two"); - - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ - json: { - blocks: [ - getCard({ text: "block number one", slotIndex: 1 }), - getCard({ text: "block number two" }), - ], - }, - }); - }); - await page.goto(`/js/index.html`); - await expect(page.locator("flows-slot")).toBeVisible(); - await expect(page.locator(".flows-card").nth(0)).toHaveText("block number two"); - await expect(page.locator(".flows-card").nth(1)).toHaveText("block number one"); - - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ - json: { - blocks: [ - getCard({ text: "block number one", slotIndex: 1 }), - getCard({ text: "block number two", slotIndex: 2 }), - ], - }, - }); - }); - await page.goto(`/js/index.html`); - await expect(page.locator("flows-slot")).toBeVisible(); - await expect(page.locator(".flows-card").nth(0)).toHaveText("block number one"); - await expect(page.locator(".flows-card").nth(1)).toHaveText("block number two"); -}); diff --git a/workspaces/e2e/tests/js/tour.spec.ts b/workspaces/e2e/tests/js/tour.spec.ts deleted file mode 100644 index 6d442bcc..00000000 --- a/workspaces/e2e/tests/js/tour.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { Block } from "@flows/shared"; -import { expect, test } from "@playwright/test"; - -test.beforeEach(async ({ page }) => { - await page.routeWebSocket( - (url) => url.pathname === "/ws/sdk/block-updates", - () => {}, - ); -}); - -const tour: Block = { - id: "t", - type: "tour", - data: {}, - exitNodes: ["complete", "cancel"], - slottable: false, - tourBlocks: [ - { - id: "t1", - type: "tour-component", - componentType: "Modal", - data: { - title: "Hello", - body: "", - continueText: "Continue", - previousText: "Previous", - showCloseButton: true, - }, - slottable: false, - }, - { - id: "t2", - type: "tour-component", - componentType: "Modal", - data: { - title: "World", - body: "", - continueText: "Continue", - previousText: "Previous", - showCloseButton: false, - }, - slottable: false, - }, - ], -}; - -test("should show tour first step without previous button", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tour] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByRole("button", { name: "Close" })).toBeVisible(); - await expect(page.getByText("Continue")).toBeVisible(); - await expect(page.getByText("Previous")).toBeHidden(); -}); -test("should be able to switch between tour steps", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tour] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByText("World")).toBeHidden(); - await page.getByText("Continue").click(); - await expect(page.getByText("Hello")).toBeHidden(); - await expect(page.getByText("World")).toBeVisible(); - await expect(page.getByRole("button", { name: "Close" })).toBeHidden(); - await expect(page.getByText("Continue")).toBeVisible(); - await expect(page.getByText("Previous")).toBeVisible(); - await page.getByText("Previous").click(); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByText("World")).toBeHidden(); -}); -test("should be able to close the tour", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tour] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Hello")).toBeVisible(); - await page.getByRole("button", { name: "Close" }).click(); - await expect(page.getByText("Hello")).toBeHidden(); -}); -test("should be able to complete the tour", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tour] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Hello")).toBeVisible(); - await page.getByText("Continue").click(); - await expect(page.getByText("World")).toBeVisible(); - await page.getByText("Continue").click(); - await expect(page.getByText("World")).toBeHidden(); -}); - -test("should send current step event", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tour] } }); - }); - await page.goto(`/js/index.html`); - const eventReq1 = page.waitForRequest( - (req) => - req.url().includes("/v2/sdk/events") && - req.postDataJSON().name === "tour-update" && - req.postDataJSON().properties.currentTourIndex === 1, - ); - await page.getByText("Continue").click(); - await eventReq1; - const eventReq2 = page.waitForRequest( - (req) => - req.url().includes("/v2/sdk/events") && - req.postDataJSON().name === "tour-update" && - req.postDataJSON().properties.currentTourIndex === 0, - ); - await page.getByText("Previous").click(); - await eventReq2; -}); diff --git a/workspaces/e2e/tests/js/wait.spec.ts b/workspaces/e2e/tests/js/wait.spec.ts deleted file mode 100644 index e31a3f68..00000000 --- a/workspaces/e2e/tests/js/wait.spec.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { Block } from "@flows/shared"; -import { expect, test } from "@playwright/test"; - -test.beforeEach(async ({ page }) => { - await page.routeWebSocket( - (url) => url.pathname === "/ws/sdk/block-updates", - () => {}, - ); -}); - -const tourWithWait: Block = { - id: "t", - type: "tour", - data: {}, - exitNodes: ["complete", "cancel"], - slottable: false, - tourBlocks: [ - { - id: "t1", - type: "tour-component", - componentType: "Modal", - data: { - title: "Hello", - body: "", - continueText: "Continue", - previousText: "Previous", - showCloseButton: true, - }, - slottable: false, - }, - { - id: "t2", - type: "wait", - slottable: false, - data: {}, - tourWait: { - interaction: "click", - element: "h1", - page: { - operator: "contains", - value: ["?correct=true"], - }, - }, - }, - { - id: "t3", - type: "tour-component", - componentType: "Modal", - data: { - title: "World", - body: "", - continueText: "Continue", - previousText: "Previous", - showCloseButton: false, - }, - slottable: false, - }, - ], -}; - -test("should wait for next step", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tourWithWait] } }); - }); - await page.goto(`/js/index.html?correct=true`); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByText("World")).toBeHidden(); - await page.getByText("Continue").click(); - await expect(page.getByText("Hello")).toBeHidden(); - await expect(page.getByText("World")).toBeHidden(); - await page.locator("h1").click(); - await expect(page.getByText("Hello")).toBeHidden(); - await expect(page.getByText("World")).toBeVisible(); -}); -test("should not trigger wait on incorrect page", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tourWithWait] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByText("World")).toBeHidden(); - await page.getByText("Continue").click(); - await expect(page.getByText("Hello")).toBeHidden(); - await expect(page.getByText("World")).toBeHidden(); - await page.locator("h1").click(); - await expect(page.getByText("Hello")).toBeHidden(); - await expect(page.getByText("World")).toBeHidden(); -}); -test("should not trigger wait by clicking on incorrect element", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tourWithWait] } }); - }); - await page.goto(`/js/index.html?correct=true`); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByText("World")).toBeHidden(); - await page.getByText("Continue").click(); - await expect(page.getByText("Hello")).toBeHidden(); - await expect(page.getByText("World")).toBeHidden(); - await page.locator("h2").click(); - await expect(page.getByText("Hello")).toBeHidden(); - await expect(page.getByText("World")).toBeHidden(); -}); - -const tourWithModalWait: Block = { - id: "t", - type: "tour", - data: {}, - exitNodes: ["complete", "cancel"], - slottable: false, - tourBlocks: [ - { - id: "t1", - type: "tour-component", - componentType: "Modal", - data: { - title: "Hello", - body: "", - showCloseButton: true, - hideOverlay: true, - }, - tourWait: { - interaction: "click", - element: "h1", - page: { - operator: "contains", - value: ["?correct=true"], - }, - }, - slottable: false, - }, - { - id: "t2", - type: "tour-component", - componentType: "Modal", - data: { - title: "World", - body: "", - continueText: "Continue", - previousText: "Previous", - showCloseButton: false, - }, - slottable: false, - }, - ], -}; - -test("should wait for modal wait", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tourWithModalWait] } }); - }); - await page.goto(`/js/index.html?correct=true`); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByText("World")).toBeHidden(); - await expect(page.getByText("Continue")).toBeHidden(); - await page.locator("h1").click(); - await expect(page.getByText("Hello")).toBeHidden(); - await expect(page.getByText("World")).toBeVisible(); -}); -test("should not trigger modal wait on incorrect page", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tourWithModalWait] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByText("World")).toBeHidden(); - await expect(page.getByText("Continue")).toBeHidden(); - await page.locator("h1").click(); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByText("World")).toBeHidden(); -}); -test("should not trigger modal wait by clicking on incorrect element", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [tourWithModalWait] } }); - }); - await page.goto(`/js/index.html?correct=true`); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByText("World")).toBeHidden(); - await expect(page.getByText("Continue")).toBeHidden(); - await page.locator("h2").click(); - await expect(page.getByText("Hello")).toBeVisible(); - await expect(page.getByText("World")).toBeHidden(); -}); diff --git a/workspaces/e2e/tests/js/websocket.spec.ts b/workspaces/e2e/tests/js/websocket.spec.ts deleted file mode 100644 index 068d3566..00000000 --- a/workspaces/e2e/tests/js/websocket.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Block, BlockUpdatesPayload } from "@flows/shared"; -import { expect, test, WebSocketRoute } from "@playwright/test"; - -const block: Block = { - id: "w", - type: "component", - componentType: "Modal", - data: { title: "Hello world", body: "" }, - exitNodes: [], - slottable: false, -}; - -let ws: WebSocketRoute | null = null; -test.beforeEach(async ({ page }) => { - await page.routeWebSocket( - (url) => url.pathname === "/ws/sdk/block-updates", - (_ws) => { - ws = _ws; - }, - ); -}); - -test("should display block that is received through websocket", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Hello world")).toBeHidden(); - const payload: BlockUpdatesPayload = { - exitedBlockIds: [], - updatedBlocks: [block], - }; - await (ws as unknown as WebSocketRoute).send(JSON.stringify(payload)); - await expect(page.getByText("Hello world")).toBeVisible(); -}); -test("should exit block that is received through websocket", async ({ page }) => { - await page.route("**/v2/sdk/blocks", (route) => { - route.fulfill({ json: { blocks: [block] } }); - }); - await page.goto(`/js/index.html`); - await expect(page.getByText("Hello world")).toBeVisible(); - const payload: BlockUpdatesPayload = { - exitedBlockIds: ["w"], - updatedBlocks: [], - }; - await (ws as unknown as WebSocketRoute).send(JSON.stringify(payload)); - await expect(page.getByText("Hello world")).toBeHidden(); -}); diff --git a/workspaces/e2e/tests/page-targeting.spec.ts b/workspaces/e2e/tests/page-targeting.spec.ts new file mode 100644 index 00000000..c1111e98 --- /dev/null +++ b/workspaces/e2e/tests/page-targeting.spec.ts @@ -0,0 +1,193 @@ +import { expect, test } from "@playwright/test"; +import { Block, TourStep } from "@flows/shared"; + +test.beforeEach(async ({ page }) => { + await page.routeWebSocket( + (url) => url.pathname === "/ws/sdk/block-updates", + () => {}, + ); +}); + +const floatingWorkflowBlock: Block = { + id: "w", + type: "component", + componentType: "Modal", + data: { title: "Workflow block", body: "" }, + exitNodes: [], + slottable: false, +}; + +const slotBlock: Block = { + id: "w", + type: "component", + componentType: "Card", + data: { text: "slottable block" }, + exitNodes: [], + slottable: true, + slotId: "my-slot", +}; + +const getTour = (steps: TourStep[]): Block => ({ + id: "wt", + type: "tour", + data: {}, + exitNodes: ["complete", "cancel"], + slottable: false, + tourBlocks: steps, +}); + +const floatingTourModalBlock: TourStep = { + id: "t", + type: "tour-component", + componentType: "Modal", + data: { title: "Tour block", body: "" }, + slottable: false, +}; + +const slotTourModalBlock: TourStep = { + id: "t", + type: "tour-component", + componentType: "Card", + data: { text: "slottable tour block" }, + slottable: true, + slotId: "my-slot", +}; + +const run = (packageName: string) => { + // Floating workflow + test(`${packageName} - should show workflow block without page targeting`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [floatingWorkflowBlock] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Workflow block")).toBeVisible(); + }); + + test(`${packageName} - should not show workflow block with incorrect page targeting`, async ({ + page, + }) => { + const block: Block = { + ...floatingWorkflowBlock, + page_targeting_operator: "contains", + page_targeting_values: ["/wrong"], + }; + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [block] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Workflow block")).toBeHidden(); + }); + + test(`${packageName} - should show workflow block with correct page targeting`, async ({ + page, + }) => { + const block: Block = { + ...floatingWorkflowBlock, + page_targeting_operator: "contains", + page_targeting_values: [`/${packageName}.html`], + }; + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [block] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Workflow block")).toBeVisible(); + }); + + // Workflow slot + test(`${packageName} - should not show workflow block in slot with incorrect page targeting`, async ({ + page, + }) => { + const block: Block = { + ...slotBlock, + page_targeting_operator: "contains", + page_targeting_values: ["/wrong"], + }; + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [block] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("slottable block")).toBeHidden(); + }); + test(`${packageName} - should show workflow block in slot with correct page targeting`, async ({ + page, + }) => { + const block: Block = { + ...slotBlock, + page_targeting_operator: "contains", + page_targeting_values: [`/${packageName}.html`], + }; + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [block] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("slottable block")).toBeVisible(); + }); + + // Floating tour + test(`${packageName} - should show tour block without page targeting`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [getTour([floatingTourModalBlock])] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Tour block")).toBeVisible(); + }); + test(`${packageName} - should not show tour block with incorrect page targeting`, async ({ + page, + }) => { + const block: TourStep = { + ...floatingTourModalBlock, + page_targeting_operator: "contains", + page_targeting_values: ["/wrong"], + }; + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [getTour([block])] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Tour block")).toBeHidden(); + }); + test(`${packageName} - should show tour block with correct page targeting`, async ({ page }) => { + const block: TourStep = { + ...floatingTourModalBlock, + page_targeting_operator: "contains", + page_targeting_values: [`/${packageName}.html`], + }; + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [getTour([block])] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Tour block")).toBeVisible(); + }); + + // Tour slot + test(`${packageName} - should not show tour block in slot with incorrect page targeting`, async ({ + page, + }) => { + const block: TourStep = { + ...slotTourModalBlock, + page_targeting_operator: "contains", + page_targeting_values: ["/wrong"], + }; + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [getTour([block])] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Tour block")).toBeHidden(); + }); + test(`${packageName} - should show tour block in slot with correct page targeting`, async ({ + page, + }) => { + const block: TourStep = { + ...slotTourModalBlock, + page_targeting_operator: "contains", + page_targeting_values: [`/${packageName}.html`], + }; + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [getTour([block])] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Tour block")).toBeVisible(); + }); +}; + +run("js"); +run("react"); diff --git a/workspaces/e2e/tests/slot.spec.ts b/workspaces/e2e/tests/slot.spec.ts new file mode 100644 index 00000000..cc5f5d41 --- /dev/null +++ b/workspaces/e2e/tests/slot.spec.ts @@ -0,0 +1,82 @@ +import { Block } from "@flows/shared"; +import test, { expect } from "@playwright/test"; + +test.beforeEach(async ({ page }) => { + await page.routeWebSocket( + (url) => url.pathname === "/ws/sdk/block-updates", + () => {}, + ); +}); + +const getCard = (props: { slotIndex?: number; text: string }): Block => ({ + id: "c", + type: "component", + componentType: "Card", + data: { text: props.text }, + exitNodes: [], + slottable: true, + slotId: "my-slot", + slotIndex: props.slotIndex, +}); + +const run = (packageName: string) => { + test(`${packageName} - should render empty slot`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Slot placeholder")).toBeVisible(); + }); + test(`${packageName} - should render block in slot and hide placeholder`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [getCard({ text: "Hello world" })] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Slot placeholder")).toBeHidden(); + await expect(page.getByText("Hello world")).toBeVisible(); + await expect(page.locator(".flows-card")).toBeVisible(); + }); + test(`${packageName} - should sort blocks by slotIndex`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ + json: { + blocks: [getCard({ text: "block number one" }), getCard({ text: "block number two" })], + }, + }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.locator(".flows-card").nth(0)).toHaveText("block number one"); + await expect(page.locator(".flows-card").nth(1)).toHaveText("block number two"); + + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ + json: { + blocks: [ + getCard({ text: "block number one", slotIndex: 1 }), + getCard({ text: "block number two" }), + ], + }, + }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.locator(".flows-card").nth(0)).toHaveText("block number two"); + await expect(page.locator(".flows-card").nth(1)).toHaveText("block number one"); + + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ + json: { + blocks: [ + getCard({ text: "block number one", slotIndex: 1 }), + getCard({ text: "block number two", slotIndex: 2 }), + ], + }, + }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.locator(".flows-card").nth(0)).toHaveText("block number one"); + await expect(page.locator(".flows-card").nth(1)).toHaveText("block number two"); + }); +}; + +run("js"); +run("react"); diff --git a/workspaces/e2e/tests/tour.spec.ts b/workspaces/e2e/tests/tour.spec.ts new file mode 100644 index 00000000..dc805cae --- /dev/null +++ b/workspaces/e2e/tests/tour.spec.ts @@ -0,0 +1,121 @@ +import { Block } from "@flows/shared"; +import { expect, test } from "@playwright/test"; + +test.beforeEach(async ({ page }) => { + await page.routeWebSocket( + (url) => url.pathname === "/ws/sdk/block-updates", + () => {}, + ); +}); + +const tour: Block = { + id: "t", + type: "tour", + data: {}, + exitNodes: ["complete", "cancel"], + slottable: false, + tourBlocks: [ + { + id: "t1", + type: "tour-component", + componentType: "Modal", + data: { + title: "Hello", + body: "", + continueText: "Continue", + previousText: "Previous", + showCloseButton: true, + }, + slottable: false, + }, + { + id: "t2", + type: "tour-component", + componentType: "Modal", + data: { + title: "World", + body: "", + continueText: "Continue", + previousText: "Previous", + showCloseButton: false, + }, + slottable: false, + }, + ], +}; + +const run = (packageName: string) => { + test(`${packageName} - should show tour first step without previous button`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tour] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByRole("button", { name: "Close" })).toBeVisible(); + await expect(page.getByText("Continue")).toBeVisible(); + await expect(page.getByText("Previous")).toBeHidden(); + }); + test(`${packageName} - should be able to switch between tour steps`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tour] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByText("World")).toBeHidden(); + await page.getByText("Continue").click(); + await expect(page.getByText("Hello")).toBeHidden(); + await expect(page.getByText("World")).toBeVisible(); + await expect(page.getByRole("button", { name: "Close" })).toBeHidden(); + await expect(page.getByText("Continue")).toBeVisible(); + await expect(page.getByText("Previous")).toBeVisible(); + await page.getByText("Previous").click(); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByText("World")).toBeHidden(); + }); + test(`${packageName} - should be able to close the tour`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tour] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Hello")).toBeVisible(); + await page.getByRole("button", { name: "Close" }).click(); + await expect(page.getByText("Hello")).toBeHidden(); + }); + test(`${packageName} - should be able to complete the tour`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tour] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Hello")).toBeVisible(); + await page.getByText("Continue").click(); + await expect(page.getByText("World")).toBeVisible(); + await page.getByText("Continue").click(); + await expect(page.getByText("World")).toBeHidden(); + }); + + test(`${packageName} - should send current step event`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tour] } }); + }); + await page.goto(`/${packageName}.html`); + const eventReq1 = page.waitForRequest( + (req) => + req.url().includes("/v2/sdk/events") && + req.postDataJSON().name === "tour-update" && + req.postDataJSON().properties.currentTourIndex === 1, + ); + await page.getByText("Continue").click(); + await eventReq1; + const eventReq2 = page.waitForRequest( + (req) => + req.url().includes("/v2/sdk/events") && + req.postDataJSON().name === "tour-update" && + req.postDataJSON().properties.currentTourIndex === 0, + ); + await page.getByText("Previous").click(); + await eventReq2; + }); +}; + +run("js"); +run("react"); diff --git a/workspaces/e2e/tests/wait.spec.ts b/workspaces/e2e/tests/wait.spec.ts new file mode 100644 index 00000000..16927cc2 --- /dev/null +++ b/workspaces/e2e/tests/wait.spec.ts @@ -0,0 +1,191 @@ +import { Block } from "@flows/shared"; +import { expect, test } from "@playwright/test"; + +test.beforeEach(async ({ page }) => { + await page.routeWebSocket( + (url) => url.pathname === "/ws/sdk/block-updates", + () => {}, + ); +}); + +const tourWithWait: Block = { + id: "t", + type: "tour", + data: {}, + exitNodes: ["complete", "cancel"], + slottable: false, + tourBlocks: [ + { + id: "t1", + type: "tour-component", + componentType: "Modal", + data: { + title: "Hello", + body: "", + continueText: "Continue", + previousText: "Previous", + showCloseButton: true, + }, + slottable: false, + }, + { + id: "t2", + type: "wait", + slottable: false, + data: {}, + tourWait: { + interaction: "click", + element: "h1", + page: { + operator: "contains", + value: ["?correct=true"], + }, + }, + }, + { + id: "t3", + type: "tour-component", + componentType: "Modal", + data: { + title: "World", + body: "", + continueText: "Continue", + previousText: "Previous", + showCloseButton: false, + }, + slottable: false, + }, + ], +}; + +const tourWithModalWait: Block = { + id: "t", + type: "tour", + data: {}, + exitNodes: ["complete", "cancel"], + slottable: false, + tourBlocks: [ + { + id: "t1", + type: "tour-component", + componentType: "Modal", + data: { + title: "Hello", + body: "", + showCloseButton: true, + hideOverlay: true, + }, + tourWait: { + interaction: "click", + element: "h1", + page: { + operator: "contains", + value: ["?correct=true"], + }, + }, + slottable: false, + }, + { + id: "t2", + type: "tour-component", + componentType: "Modal", + data: { + title: "World", + body: "", + continueText: "Continue", + previousText: "Previous", + showCloseButton: false, + }, + slottable: false, + }, + ], +}; + +const run = (packageName: string) => { + test(`${packageName} - should wait for next step`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tourWithWait] } }); + }); + await page.goto(`/${packageName}.html?correct=true`); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByText("World")).toBeHidden(); + await page.getByText("Continue").click(); + await expect(page.getByText("Hello")).toBeHidden(); + await expect(page.getByText("World")).toBeHidden(); + await page.locator("h1").click(); + await expect(page.getByText("Hello")).toBeHidden(); + await expect(page.getByText("World")).toBeVisible(); + }); + test(`${packageName} - should not trigger wait on incorrect page`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tourWithWait] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByText("World")).toBeHidden(); + await page.getByText("Continue").click(); + await expect(page.getByText("Hello")).toBeHidden(); + await expect(page.getByText("World")).toBeHidden(); + await page.locator("h1").click(); + await expect(page.getByText("Hello")).toBeHidden(); + await expect(page.getByText("World")).toBeHidden(); + }); + test(`${packageName} - should not trigger wait by clicking on incorrect element`, async ({ + page, + }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tourWithWait] } }); + }); + await page.goto(`/${packageName}.html?correct=true`); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByText("World")).toBeHidden(); + await page.getByText("Continue").click(); + await expect(page.getByText("Hello")).toBeHidden(); + await expect(page.getByText("World")).toBeHidden(); + await page.locator("h2").click(); + await expect(page.getByText("Hello")).toBeHidden(); + await expect(page.getByText("World")).toBeHidden(); + }); + + test(`${packageName} - should wait for modal wait`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tourWithModalWait] } }); + }); + await page.goto(`/${packageName}.html?correct=true`); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByText("World")).toBeHidden(); + await expect(page.getByText("Continue")).toBeHidden(); + await page.locator("h1").click(); + await expect(page.getByText("Hello")).toBeHidden(); + await expect(page.getByText("World")).toBeVisible(); + }); + test(`${packageName} - should not trigger modal wait on incorrect page`, async ({ page }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tourWithModalWait] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByText("World")).toBeHidden(); + await expect(page.getByText("Continue")).toBeHidden(); + await page.locator("h1").click(); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByText("World")).toBeHidden(); + }); + test(`${packageName} - should not trigger modal wait by clicking on incorrect element`, async ({ + page, + }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [tourWithModalWait] } }); + }); + await page.goto(`/${packageName}.html?correct=true`); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByText("World")).toBeHidden(); + await expect(page.getByText("Continue")).toBeHidden(); + await page.locator("h2").click(); + await expect(page.getByText("Hello")).toBeVisible(); + await expect(page.getByText("World")).toBeHidden(); + }); +}; + +run("js"); +run("react"); diff --git a/workspaces/e2e/tests/websocket.spec.ts b/workspaces/e2e/tests/websocket.spec.ts new file mode 100644 index 00000000..87508ae7 --- /dev/null +++ b/workspaces/e2e/tests/websocket.spec.ts @@ -0,0 +1,57 @@ +import { Block, BlockUpdatesPayload } from "@flows/shared"; +import { expect, test, WebSocketRoute } from "@playwright/test"; + +const block: Block = { + id: "w", + type: "component", + componentType: "Modal", + data: { title: "Hello world", body: "" }, + exitNodes: [], + slottable: false, +}; + +let ws: WebSocketRoute | null = null; +test.beforeEach(async ({ page }) => { + await page.routeWebSocket( + (url) => url.pathname === "/ws/sdk/block-updates", + (_ws) => { + ws = _ws; + }, + ); +}); + +const run = (packageName: string) => { + test(`${packageName} - should display block that is received through websocket`, async ({ + page, + }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Hello world")).toBeHidden(); + const payload: BlockUpdatesPayload = { + exitedBlockIds: [], + updatedBlocks: [block], + }; + await (ws as unknown as WebSocketRoute).send(JSON.stringify(payload)); + await expect(page.getByText("Hello world")).toBeVisible(); + }); + test(`${packageName} - should exit block that is received through websocket`, async ({ + page, + }) => { + await page.route("**/v2/sdk/blocks", (route) => { + route.fulfill({ json: { blocks: [block] } }); + }); + await page.goto(`/${packageName}.html`); + await expect(page.getByText("Hello world")).toBeVisible(); + const payload: BlockUpdatesPayload = { + exitedBlockIds: ["w"], + updatedBlocks: [], + }; + await (ws as unknown as WebSocketRoute).send(JSON.stringify(payload)); + await expect(page.getByText("Hello world")).toBeHidden(); + }); +}; + +run("js"); +run("react"); diff --git a/workspaces/e2e/tsconfig.json b/workspaces/e2e/tsconfig.json index f14db07a..9af19da7 100644 --- a/workspaces/e2e/tsconfig.json +++ b/workspaces/e2e/tsconfig.json @@ -1,4 +1,7 @@ { "extends": "../../tsconfig.base.json", - "include": ["tests/**/*"] + "compilerOptions": { + "jsx": "react-jsx" + }, + "include": ["tests/**/*", "pages/**/*"] } diff --git a/workspaces/e2e/vite.config.js b/workspaces/e2e/vite.config.js index be0ac950..65f9a330 100644 --- a/workspaces/e2e/vite.config.js +++ b/workspaces/e2e/vite.config.js @@ -1,8 +1,11 @@ +import react from "@vitejs/plugin-react"; + /** @type {import('vite').UserConfig} */ export default { + plugins: [react()], server: { port: 3000, + watch: null, }, - root: "./tests", - publicDir: "../", + root: "./pages", }; diff --git a/workspaces/react-components/package.json b/workspaces/react-components/package.json index 13b021cf..a3ca8d64 100644 --- a/workspaces/react-components/package.json +++ b/workspaces/react-components/package.json @@ -27,7 +27,8 @@ "tsc": "tsc -p tsconfig.json", "version": "pnpm version", "test": "jest --passWithNoTests", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "prepare": "pnpm build" }, "license": "MIT", "sideEffects": false,