diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 5be360b99..987e5c934 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -29,7 +29,7 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'npm' - run: npm ci - - run: npx playwright install --with-deps --only-shell chromium + - run: npx playwright install --with-deps chromium - run: npm run lint - run: npm run test - run: npm run check diff --git a/package-lock.json b/package-lock.json index b0c270b71..1302c74c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,18 +19,19 @@ "@biomejs/biome": "~2.3.8", "@custom-elements-manifest/analyzer": "^0.11.0", "@igniteui/material-icons-extended": "^3.1.0", - "@open-wc/testing": "^4.0.0", + "@open-wc/semantic-dom-diff": "^0.20.1", "@storybook/addon-a11y": "^10.1.2", "@storybook/addon-docs": "^10.1.2", "@storybook/addon-links": "^10.1.2", "@storybook/web-components-vite": "^10.1.2", - "@types/mocha": "^10.0.10", - "@web/dev-server-esbuild": "^1.0.4", - "@web/test-runner": "^0.20.2", - "@web/test-runner-playwright": "^0.11.1", + "@types/chai-dom": "^1.11.3", + "@vitest/browser-playwright": "^4.0.14", + "@vitest/coverage-v8": "^4.0.15", "autoprefixer": "^10.4.22", "browser-sync": "^3.0.4", "cem-plugin-expanded-types": "^1.4.0", + "chai-a11y-axe": "^1.5.0", + "chai-dom": "^1.12.1", "concurrently": "^9.2.1", "custom-element-jet-brains-integration": "^1.7.0", "custom-element-vs-code-integration": "^1.5.0", @@ -49,7 +50,6 @@ "prettier": "^3.7.3", "rimraf": "^6.1.2", "sass-embedded": "~1.93.3", - "sinon": "^21.0.0", "storybook": "^10.1.2", "stylelint": "^16.26.1", "stylelint-config-standard-scss": "^16.0.0", @@ -151,6 +151,16 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@biomejs/biome": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.8.tgz", @@ -322,22 +332,22 @@ "license": "(Apache-2.0 AND BSD-3-Clause)" }, "node_modules/@cacheable/memory": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.5.tgz", - "integrity": "sha512-fkiAxCvssEyJZ5fxX4tcdZFRmW9JehSTGvvqmXn6rTzG5cH6V/3C4ad8yb01vOjp2xBydHkHrgpW0qeGtzt6VQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.6.tgz", + "integrity": "sha512-7e8SScMocHxcAb8YhtkbMhGG+EKLRIficb1F5sjvhSYsWTZGxvg4KIDp8kgxnV2PUJ3ddPe6J9QESjKvBWRDkg==", "dev": true, "license": "MIT", "dependencies": { - "@cacheable/utils": "^2.3.0", - "@keyv/bigmap": "^1.1.0", - "hookified": "^1.12.2", + "@cacheable/utils": "^2.3.2", + "@keyv/bigmap": "^1.3.0", + "hookified": "^1.13.0", "keyv": "^5.5.4" } }, "node_modules/@cacheable/utils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.3.1.tgz", - "integrity": "sha512-38NJXjIr4W1Sghun8ju+uYWD8h2c61B4dKwfnQHVDFpAJ9oS28RpfqZQJ6Dgd3RceGkILDY9YT+72HJR3LoeSQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.3.2.tgz", + "integrity": "sha512-8kGE2P+HjfY8FglaOiW+y8qxcaQAfAhVML+i66XJR3YX5FtyDqn6Txctr3K2FrbxLKixRRYYBWMbuGciOhYNDg==", "dev": true, "license": "MIT", "dependencies": { @@ -648,9 +658,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", + "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", "cpu": [ "ppc64" ], @@ -665,9 +675,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", + "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", "cpu": [ "arm" ], @@ -682,9 +692,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", + "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", "cpu": [ "arm64" ], @@ -699,9 +709,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", + "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", "cpu": [ "x64" ], @@ -716,9 +726,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", + "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", "cpu": [ "arm64" ], @@ -733,9 +743,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", + "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", "cpu": [ "x64" ], @@ -750,9 +760,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", + "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", "cpu": [ "arm64" ], @@ -767,9 +777,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", + "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", "cpu": [ "x64" ], @@ -784,9 +794,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", + "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", "cpu": [ "arm" ], @@ -801,9 +811,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", + "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", "cpu": [ "arm64" ], @@ -818,9 +828,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", + "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", "cpu": [ "ia32" ], @@ -835,9 +845,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", + "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", "cpu": [ "loong64" ], @@ -852,9 +862,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", + "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", "cpu": [ "mips64el" ], @@ -869,9 +879,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", + "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", "cpu": [ "ppc64" ], @@ -886,9 +896,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", + "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", "cpu": [ "riscv64" ], @@ -903,9 +913,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", + "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", "cpu": [ "s390x" ], @@ -920,9 +930,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", + "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", "cpu": [ "x64" ], @@ -937,9 +947,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", + "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", "cpu": [ "arm64" ], @@ -954,9 +964,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", + "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", "cpu": [ "x64" ], @@ -971,9 +981,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", + "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", "cpu": [ "arm64" ], @@ -988,9 +998,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", + "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", "cpu": [ "x64" ], @@ -1005,9 +1015,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", + "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", "cpu": [ "arm64" ], @@ -1022,9 +1032,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", + "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", "cpu": [ "x64" ], @@ -1039,9 +1049,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", + "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", "cpu": [ "arm64" ], @@ -1056,9 +1066,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", + "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", "cpu": [ "ia32" ], @@ -1073,9 +1083,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", + "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", "cpu": [ "x64" ], @@ -1089,16 +1099,6 @@ "node": ">=18" } }, - "node_modules/@esm-bundle/chai": { - "version": "4.3.4-fix.0", - "resolved": "https://registry.npmjs.org/@esm-bundle/chai/-/chai-4.3.4-fix.0.tgz", - "integrity": "sha512-26SKdM4uvDWlY8/OOOxSB1AqQWeBosCX3wRYUZO7enTAj03CtVxIiCimYVG2WpULcyV51qapK4qTovwkUr5Mlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^4.2.12" - } - }, "node_modules/@floating-ui/core": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", @@ -1125,16 +1125,16 @@ "license": "MIT" }, "node_modules/@gerrit0/mini-shiki": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.17.1.tgz", - "integrity": "sha512-u7gBnLsvhyVpwR4G8LcSHDlPn8Hg8zNeuzzR4+p2AxvQrQ+BDGo/mLMCpo58VFiIbl8+ie42fqunDclZ4RxNWw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.18.0.tgz", + "integrity": "sha512-zTAG1cXK5Q+T6CBEa8mqEnCx/H9rrpWEn+vhMbWikzmeO2jltY6zVE2m9YCO+xDi+P0vpBrOG1Xgi8AZtlNoUA==", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/engine-oniguruma": "^3.17.1", - "@shikijs/langs": "^3.17.1", - "@shikijs/themes": "^3.17.1", - "@shikijs/types": "^3.17.1", + "@shikijs/engine-oniguruma": "^3.18.0", + "@shikijs/langs": "^3.18.0", + "@shikijs/themes": "^3.18.0", + "@shikijs/types": "^3.18.0", "@shikijs/vscode-textmate": "^10.0.2" } }, @@ -1290,13 +1290,6 @@ "@lit-labs/ssr-dom-shim": "^1.4.0" } }, - "node_modules/@mdn/browser-compat-data": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-4.2.1.tgz", - "integrity": "sha512-EWUguj2kd7ldmrF9F+vI5hUOralPd+sdsUnYbRy33vZTuZkduC1shE9TtEMEjAQwyfyMb4ole5KtjF8MsnQOlA==", - "dev": true, - "license": "CC0-1.0" - }, "node_modules/@mdx-js/react": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", @@ -1316,15 +1309,15 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", - "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, @@ -1366,24 +1359,6 @@ "node": ">= 8" } }, - "node_modules/@open-wc/dedupe-mixin": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@open-wc/dedupe-mixin/-/dedupe-mixin-2.0.1.tgz", - "integrity": "sha512-+R4VxvceUxHAUJXJQipkkoV9fy10vNo+OnUnGKZnVmcwxMl460KLzytnUM4S35SI073R0yZQp9ra0MbPUwVcEA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@open-wc/scoped-elements": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@open-wc/scoped-elements/-/scoped-elements-3.0.6.tgz", - "integrity": "sha512-w1ayJaUUmBw8tALtqQ6cBueld+op+bufujzbrOdH0uCTXnSQkONYZzOH+9jyQ8auVgKLqcxZ8oU6SzfqQhQkPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@open-wc/dedupe-mixin": "^2.0.0", - "lit": "^3.0.0" - } - }, "node_modules/@open-wc/semantic-dom-diff": { "version": "0.20.1", "resolved": "https://registry.npmjs.org/@open-wc/semantic-dom-diff/-/semantic-dom-diff-0.20.1.tgz", @@ -1395,37 +1370,10 @@ "@web/test-runner-commands": "^0.9.0" } }, - "node_modules/@open-wc/testing": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@open-wc/testing/-/testing-4.0.0.tgz", - "integrity": "sha512-KI70O0CJEpBWs3jrTju4BFCy7V/d4tFfYWkg8pMzncsDhD7TYNHLw5cy+s1FHXIgVFetnMDhPpwlKIPvtTQW7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@esm-bundle/chai": "^4.3.4-fix.0", - "@open-wc/semantic-dom-diff": "^0.20.0", - "@open-wc/testing-helpers": "^3.0.0", - "@types/chai-dom": "^1.11.0", - "@types/sinon-chai": "^3.2.3", - "chai-a11y-axe": "^1.5.0" - } - }, - "node_modules/@open-wc/testing-helpers": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@open-wc/testing-helpers/-/testing-helpers-3.0.1.tgz", - "integrity": "sha512-hyNysSatbgT2FNxHJsS3rGKcLEo6+HwDFu1UQL6jcSQUabp/tj3PyX7UnXL3H5YGv0lJArdYLSnvjLnjn3O2fw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@open-wc/scoped-elements": "^3.0.2", - "lit": "^2.0.0 || ^3.0.0", - "lit-html": "^2.0.0 || ^3.0.0" - } - }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.13.2.tgz", - "integrity": "sha512-vWd1NEaclg/t2DtEmYzRRBNQOueMI8tixw/fSNZ9XETXLRJiAjQMYpYeflQdRASloGze6ZelHE/wIBNt4S+pkw==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.14.2.tgz", + "integrity": "sha512-bTrdE4Z1JcGwPxBOaGbxRbpOHL8/xPVJTTq3/bAZO2euWX0X7uZ+XxsbC+5jUDMhLenqdFokgE1akHEU4xsh6A==", "cpu": [ "arm" ], @@ -1437,9 +1385,9 @@ ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.13.2.tgz", - "integrity": "sha512-jxZrYcxgpI6IuQpguQVAQNrZfUyiYfMVqR4pKVU3PRLCM7AsfXNKp0TIgcvp+l6dYVdoZ1MMMMa5Ayjd09rNOw==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.14.2.tgz", + "integrity": "sha512-bL7/f6YGKUvt/wzpX7ZrHCf1QerotbSG+IIb278AklXuwr6yQdfQHt7KQ8hAWqSYpB2TAbPbAa9HE4wzVyxL9Q==", "cpu": [ "arm64" ], @@ -1451,9 +1399,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.13.2.tgz", - "integrity": "sha512-RDS3HUe1FvgjNS1xfBUqiEJ8938Zb5r7iKABwxEblp3K4ufZZNAtoaHjdUH2TJ0THDmuf0OxxVUO/Y+4Ep4QfQ==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.14.2.tgz", + "integrity": "sha512-0zhMhqHz/kC6/UzMC4D9mVBz3/M9UTorbaULfHjAW5b8SUC08H01lZ5fR3OzfDbJI0ByLfiQZmbovuR/pJ8Wzg==", "cpu": [ "arm64" ], @@ -1465,9 +1413,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.13.2.tgz", - "integrity": "sha512-tDcyWtkUzkt6auJLP2dOjL84BxqHkKW4mz2lNRIGPTq7b+HBraB+m8RdRH6BgqTvbnNECOxR3XAMaKBKC8J51g==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.14.2.tgz", + "integrity": "sha512-kRJBTCQnrGy1mjO+658yMrlGYWEKi6j4JvKt92PRCoeDX0vW4jvzgoJXzZXNxZL1pCY6jIdwsn9u53v4jwpR6g==", "cpu": [ "x64" ], @@ -1479,9 +1427,9 @@ ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.13.2.tgz", - "integrity": "sha512-fpaeN8Q0kWvKns9uSMg6CcKo7cdgmWt6J91stPf8sdM+EKXzZ0YcRnWWyWF8SM16QcLUPCy5Iwt5Z8aYBGaZYA==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.14.2.tgz", + "integrity": "sha512-lpKiya7qPq5EAV5E16SJbxfhNYRCBZATGngn9mZxR2fMLDVbHISDIP2Br8eWA8M1FBJFsOGgBzxDo+42ySSNZQ==", "cpu": [ "x64" ], @@ -1493,9 +1441,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.13.2.tgz", - "integrity": "sha512-idBgJU5AvSsGOeaIWiFBKbNBjpuduHsJmrG4CBbEUNW/Ykx+ISzcuj1PHayiYX6R9stVsRhj3d2PyymfC5KWRg==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.14.2.tgz", + "integrity": "sha512-zRIf49IGs4cE9rwpVM3NxlHWquZpwQLebtc9dY9S+4+B+PSLIP95BrzdRfkspwzWC5DKZsOWpvGQjxQiLoUwGA==", "cpu": [ "arm" ], @@ -1507,9 +1455,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.13.2.tgz", - "integrity": "sha512-BlBvQUhvvIM/7s96KlKhMk0duR2sj8T7Hyii46/5QnwfN/pHwobvOL5czZ6/SKrHNB/F/qDY4hGsBuB1y7xgTg==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.14.2.tgz", + "integrity": "sha512-sF1fBrcfwoRkv1pR3Kp6D5MuBeHRPxYuzk9rhaun/50vq5nAMOaomkEm4hBbTSubfU86CoBIEbLUQ+1f7NvUVA==", "cpu": [ "arm" ], @@ -1521,9 +1469,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.13.2.tgz", - "integrity": "sha512-lUmDTmYOGpbIK+FBfZ0ySaQTo7g1Ia/WnDnQR2wi/0AtehZIg/ZZIgiT/fD0iRvKEKma612/0PVo8dXdAKaAGA==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.14.2.tgz", + "integrity": "sha512-O8iTBqz6oxf1k93Rn6WMGGQYo2jV1K81hq4N/Nke3dHE25EIEg2RKQqMz1dFrvVb2RkvD7QaUTEevbx0Lq+4wQ==", "cpu": [ "arm64" ], @@ -1535,9 +1483,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.13.2.tgz", - "integrity": "sha512-dkGzOxo+I9lA4Er6qzFgkFevl3JvwyI9i0T/PkOJHva04rb1p9dz8GPogTO9uMK4lrwLWzm/piAu+tHYC7v7+w==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.14.2.tgz", + "integrity": "sha512-HOfzpS6eUxvdch9UlXCMx2kNJWMNBjUpVJhseqAKDB1dlrfCHgexeLyBX977GLXkq2BtNXKsY3KCryy1QhRSRw==", "cpu": [ "arm64" ], @@ -1549,9 +1497,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.13.2.tgz", - "integrity": "sha512-53kWsjLkVFnoSA7COdps38pBssN48zI8LfsOvupsmQ0/4VeMYb+0Ao9O6r52PtmFZsGB3S1Qjqbjl/Pswj1a3g==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.14.2.tgz", + "integrity": "sha512-0uLG6F2zljUseQAUmlpx/9IdKpiLsSirpmrr8/aGVfiEurIJzC/1lo2HQskkM7e0VVOkXg37AjHUDLE23Fi8SA==", "cpu": [ "ppc64" ], @@ -1563,9 +1511,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.13.2.tgz", - "integrity": "sha512-MfxN6DMpvmdCbGlheJ+ihy11oTcipqDfcEIQV9ah3FGXBRCZtBOHJpQDk8qI2Y+nCXVr3Nln7OSsOzoC4+rSYQ==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.14.2.tgz", + "integrity": "sha512-Pdh0BH/E0YIK7Qg95IsAfQyU9rAoDoFh50R19zCTNfjSnwsoDMGHjmUc82udSfPo2YMnuxA+/+aglxmLQVSu2Q==", "cpu": [ "riscv64" ], @@ -1577,9 +1525,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.13.2.tgz", - "integrity": "sha512-WXrm4YiRU0ijqb72WHSjmfYaQZ7t6/kkQrFc4JtU+pUE4DZA/DEdxOuQEd4Q43VqmLvICTJWSaZMlCGQ4PSRUg==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.14.2.tgz", + "integrity": "sha512-3DLQhJ2r53rCH5cudYFqD7nh+Z6ABvld3GjbiqHhT43GMIPw3JcHekC2QunLRNjRr1G544fo1HtjTJz9rCBpyg==", "cpu": [ "riscv64" ], @@ -1591,9 +1539,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.13.2.tgz", - "integrity": "sha512-4pISWIlOFRUhWyvGCB3XUhtcwyvwGGhlXhHz7IXCXuGufaQtvR05trvw8U1ZnaPhsdPBkRhOMIedX11ayi5uXw==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.14.2.tgz", + "integrity": "sha512-G5BnAOQ5f+RUG1cvlJ4BvV+P7iKLYBv67snqgcfwD5b2N4UwJj32bt4H5JfolocWy4x3qUjEDWTIjHdE+2uZ9w==", "cpu": [ "s390x" ], @@ -1605,9 +1553,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.13.2.tgz", - "integrity": "sha512-DVo6jS8n73yNAmCsUOOk2vBeC60j2RauDXQM8p7RDl0afsEaA2le22vD8tky7iNoM5tsxfBmE4sOJXEKgpwWRw==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.14.2.tgz", + "integrity": "sha512-VirQAX2PqKrhWtQGsSDEKlPhbgh3ggjT1sWuxLk4iLFwtyA2tLEPXJNAsG0kfAS2+VSA8OyNq16wRpQlMPZ4yA==", "cpu": [ "x64" ], @@ -1619,9 +1567,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.13.2.tgz", - "integrity": "sha512-6WqrE+hQBFP35KdwQjWcZpldbTq6yJmuTVThISu+rY3+j6MaDp2ciLHTr1X68r2H/7ocOIl4k3NnOVIzeRJE3w==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.14.2.tgz", + "integrity": "sha512-q4ORcwMkpzu4EhZyka/s2TuH2QklEHAr/mIQBXzu5BACeBJZIFkICp8qrq4XVnkEZ+XhSFTvBECqfMTT/4LSkA==", "cpu": [ "x64" ], @@ -1632,10 +1580,24 @@ "linux" ] }, + "node_modules/@oxc-resolver/binding-openharmony-arm64": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.14.2.tgz", + "integrity": "sha512-ZsMIpDCxSFpUM/TwOovX5vZUkV0IukPFnrKTGaeJRuTKXMcJxMiQGCYTwd6y684Y3j55QZqIMkVM9NdCGUX6Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.13.2.tgz", - "integrity": "sha512-YpxvQmP2D+mNUkLQZbBjGz20g/pY8XoOBdPPoWMl9X68liFFjXxkPQTrZxWw4zzG/UkTM5z6dPRTyTePRsMcjw==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.14.2.tgz", + "integrity": "sha512-Lvq5ZZNvSjT3Jq/buPFMtp55eNyGlEWsq30tN+yLOfODSo6T6yAJNs6+wXtqu9PiMj4xpVtgXypHtbQ1f+t7kw==", "cpu": [ "wasm32" ], @@ -1643,16 +1605,16 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^1.0.7" + "@napi-rs/wasm-runtime": "^1.1.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.13.2.tgz", - "integrity": "sha512-1SKBw6KcCmvPBdEw1/Qdpv6eSDf23lCXTWz9VxTe6QUQ/1wR+HZR2uS4q6C8W6jnIswMTQbxpTvVwdRXl+ufeA==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.14.2.tgz", + "integrity": "sha512-7w7WHSLSSmkkYHH52QF7TrO0Z8eaIjRUrre5M56hSWRAZupCRzADZxBVMpDnHobZ8MAa2kvvDEfDbERuOK/avQ==", "cpu": [ "arm64" ], @@ -1664,9 +1626,9 @@ ] }, "node_modules/@oxc-resolver/binding-win32-ia32-msvc": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.13.2.tgz", - "integrity": "sha512-KEVV7wggDucxRn3vvyHnmTCPXoCT7vWpH18UVLTygibHJvNRP2zl5lBaQcCIdIaYYZjKt1aGI/yZqxZvHoiCdg==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.14.2.tgz", + "integrity": "sha512-hIrdlWa6tzqyfuWrxUetURBWHttBS+NMbBrGhCupc54NCXFy2ArB+0JOOaLYiI2ShKL5a3uqB7EWxmjzOuDdPQ==", "cpu": [ "ia32" ], @@ -1678,9 +1640,9 @@ ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.13.2.tgz", - "integrity": "sha512-6AAdN9v/wO5c3td1yidgNLKYlzuNgfOtEqBq60WE469bJWR7gHgG/S5aLR2pH6/gyPLs9UXtItxi934D+0Estg==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.14.2.tgz", + "integrity": "sha512-dP9aV6AZRRpg5mlg0eMuTROtttpQwj3AiegNJ/NNmMSjs+0+aLNcgkWRPhskK3vjTsthH4/+kKLpnQhSxdJkNg==", "cpu": [ "x64" ], @@ -2001,6 +1963,13 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, "node_modules/@prettier/sync": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/@prettier/sync/-/sync-0.5.5.tgz", @@ -2017,114 +1986,6 @@ "prettier": "*" } }, - "node_modules/@puppeteer/browsers": { - "version": "2.10.13", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.13.tgz", - "integrity": "sha512-a9Ruw3j3qlnB5a/zHRTkruppynxqaeE4H9WNj5eYGRWqw0ZauZ23f4W2ARf3hghF5doozyD+CRtt7XSYuYRI/Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.4.3", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.5.0", - "semver": "^7.7.3", - "tar-fs": "^3.1.1", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@puppeteer/browsers/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@puppeteer/browsers/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", - "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.53.3", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", @@ -2434,62 +2295,62 @@ ] }, "node_modules/@shikijs/core": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.18.0.tgz", - "integrity": "sha512-qxBrX2G4ctCgpvFNWMhFvbBnsWTOmwJgSqywQm0gtamp/OXSaHBjtrBomNIY5WJGXgGCPPvI7O+Y9pH/dr/p0w==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.19.0.tgz", + "integrity": "sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA==", "license": "MIT", "peer": true, "dependencies": { - "@shikijs/types": "3.18.0", + "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "node_modules/@shikijs/engine-javascript": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.18.0.tgz", - "integrity": "sha512-S87JGGXasJH1Oe9oFTqDWGcTUX+xMlf3Jzn4XbXoa6MmB19o0B8kVRd7vmhNvSkE/WuK2GTmB0I2GY526w4KxQ==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.19.0.tgz", + "integrity": "sha512-ZfWJNm2VMhKkQIKT9qXbs76RRcT0SF/CAvEz0+RkpUDAoDaCx0uFdCGzSRiD9gSlhm6AHkjdieOBJMaO2eC1rQ==", "license": "MIT", "peer": true, "dependencies": { - "@shikijs/types": "3.18.0", + "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.18.0.tgz", - "integrity": "sha512-15+O2iy+nYU/IdiBIExXuK0JJABa/8tdnRDODBmLhdygQ43aCuipN5N9vTfS8jvkMByHMR09b5jtX2la0CCoOA==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.19.0.tgz", + "integrity": "sha512-1hRxtYIJfJSZeM5ivbUXv9hcJP3PWRo5prG/V2sWwiubUKTa+7P62d2qxCW8jiVFX4pgRHhnHNp+qeR7Xl+6kg==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.18.0", + "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "node_modules/@shikijs/langs": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.18.0.tgz", - "integrity": "sha512-Deq7ZoYBtimN0M8pD5RU5TKz7DhUSTPtQOBuJpMxPDDJ+MJ7nT90DEmhDM2V0Nzp6DjfTAd+Z7ibpzr8arWqiA==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.19.0.tgz", + "integrity": "sha512-dBMFzzg1QiXqCVQ5ONc0z2ebyoi5BKz+MtfByLm0o5/nbUu3Iz8uaTCa5uzGiscQKm7lVShfZHU1+OG3t5hgwg==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.18.0" + "@shikijs/types": "3.19.0" } }, "node_modules/@shikijs/themes": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.18.0.tgz", - "integrity": "sha512-wzg6vNniXC5J4ChNBJJIZFTWxmrERJMWknehmM++0OAKJqZ41WpnO7PmPOumvMsUaL1SC08Nb/JVdaJd2aTsZg==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.19.0.tgz", + "integrity": "sha512-H36qw+oh91Y0s6OlFfdSuQ0Ld+5CgB/VE6gNPK+Hk4VRbVG/XQgkjnt4KzfnnoO6tZPtKJKHPjwebOCfjd6F8A==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.18.0" + "@shikijs/types": "3.19.0" } }, "node_modules/@shikijs/types": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.18.0.tgz", - "integrity": "sha512-YLmpuroH06TpvqRXKR0YqlI0nQ56c8+BO/m9A9ht36WRdxmML4ivUsnpXuJU7PiClLRD2M66ilY2YJ0KE+8q7A==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.19.0.tgz", + "integrity": "sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", @@ -2515,58 +2376,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", - "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1", - "type-detect": "^4.1.0" - } + "license": "MIT" }, - "node_modules/@sinonjs/samsam/node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "dev": true, - "license": "MIT" + "peer": true }, "node_modules/@storybook/addon-a11y": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-10.1.2.tgz", - "integrity": "sha512-O4Rp+ftREulVLREOKwx3IKkUiSLx+F3V5zQ++h6bzlmwRuaknMHs5P3qSnjJprYV6K8qXTRbvgR2qKBgga8Qxw==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-10.1.4.tgz", + "integrity": "sha512-go7SshAyu+pnK7Prq3UnCBCB7DxAQkPMnebsv3fnboeTZHnDXQqfmHdZ15o+pH0JsCedC05RYsdbjd2rMHMvFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2578,20 +2406,20 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^10.1.2" + "storybook": "^10.1.4" } }, "node_modules/@storybook/addon-docs": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.1.2.tgz", - "integrity": "sha512-2D89qp6WwNxbiyylixJDC9C8tU8qgRS68HFcYruSNVX3dcCoty7xVytdWJIoDdTjvYlKZZdK23eD9r7+AEA3oA==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.1.4.tgz", + "integrity": "sha512-TWLDJNLS/S3AUyTf9x0Hb8k7d+VWMJCH9dWAS0QenvJG8ga9VaehO6r+e+3YyIDbO1ev3UST3GCjh9SY8tzwRA==", "dev": true, "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/csf-plugin": "10.1.2", + "@storybook/csf-plugin": "10.1.4", "@storybook/icons": "^2.0.0", - "@storybook/react-dom-shim": "10.1.2", + "@storybook/react-dom-shim": "10.1.4", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" @@ -2601,13 +2429,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^10.1.2" + "storybook": "^10.1.4" } }, "node_modules/@storybook/addon-links": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-10.1.2.tgz", - "integrity": "sha512-xaXdzbIWeBjjlMbq2qZKiIRpp5+Rwo654xL0BczHLpKtSNvUWmvIUUuTWIMzGFi76obTsVZL5aJ22BCh9991Qw==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-10.1.4.tgz", + "integrity": "sha512-GQplzQFYhClraxH1cQDhhiJAuqAlI2loJjcnLjayS9/O2XJfEPyHc0fjkTh83zhF/nIQ6iMpFgpCsrThRUL4ag==", "dev": true, "license": "MIT", "dependencies": { @@ -2619,7 +2447,7 @@ }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.1.2" + "storybook": "^10.1.4" }, "peerDependenciesMeta": { "react": { @@ -2628,13 +2456,13 @@ } }, "node_modules/@storybook/builder-vite": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.1.2.tgz", - "integrity": "sha512-gEIduoOUQZL0xS3LJu/9WjPRppg2wptNp6ifLZiRYF6R3T0q4IBSzQ3oXIeHOcwhKUW//vRSuci2NDe1llUjMw==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.1.4.tgz", + "integrity": "sha512-3mUQoCzMuhqAIjj8fdbGlwh+GgHaFpCvU+sxL8kIxnZqflW09SuwM5kS47Y5QDzYbHAPYCPqcBFyJ4EfRuf0rw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf-plugin": "10.1.2", + "@storybook/csf-plugin": "10.1.4", "@vitest/mocker": "3.2.4", "ts-dedent": "^2.0.0" }, @@ -2643,14 +2471,14 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^10.1.2", + "storybook": "^10.1.4", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@storybook/csf-plugin": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.1.2.tgz", - "integrity": "sha512-dwFKoKsV73SEKdaA78/AZlMa8+pAt2YS8f8cAvRLtsCxus9u0sJqxR/5axlZk0KLHnoJ+exZDD/zpK/HCsqtNw==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.1.4.tgz", + "integrity": "sha512-nudIBYx8fBz+1j2Xn1pdfGcgMJ78N/1NFB4MYAxI3YEzxGnQwUjihOO1x3siAXPbjFGmnVHoBx7+6IpO3F70GA==", "dev": true, "license": "MIT", "dependencies": { @@ -2663,7 +2491,7 @@ "peerDependencies": { "esbuild": "*", "rollup": "*", - "storybook": "^10.1.2", + "storybook": "^10.1.4", "vite": "*", "webpack": "*" }, @@ -2701,9 +2529,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.1.2.tgz", - "integrity": "sha512-HxmL6rD99qaZervm3S/g0QjflSpCk31kZX6+guLBD85KzH+sgo7XNjlipNqfdzAOfWpri0rh6zEGyNI0erFlag==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.1.4.tgz", + "integrity": "sha512-PARu2HA5nYU1AkioNJNc430pz0oyaHFSSAdN3NEaWwkoGrCOo9ZpAXP9V7wlJANCi1pndbC84gSuHVnBXJBG6g==", "dev": true, "license": "MIT", "funding": { @@ -2713,13 +2541,13 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.1.2" + "storybook": "^10.1.4" } }, "node_modules/@storybook/web-components": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-10.1.2.tgz", - "integrity": "sha512-HODGLPe8PuL7ojxr8eM1owvsvdM4l4Z1/uvnXMZqQ3TWtUQfY3DQPKJ8x7perWKu2svfpWa+j3MpHrWRHUDviw==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-10.1.4.tgz", + "integrity": "sha512-2ayVY01gWQxPS4o7xos/kTkntW/ZYdyNMXOKhnalCKz9NCNLEmtX/2gFIkGwZ7zo1H2rkAqaaQK4KqbTFxZ0XA==", "dev": true, "license": "MIT", "dependencies": { @@ -2733,25 +2561,25 @@ }, "peerDependencies": { "lit": "^2.0.0 || ^3.0.0", - "storybook": "^10.1.2" + "storybook": "^10.1.4" } }, "node_modules/@storybook/web-components-vite": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-10.1.2.tgz", - "integrity": "sha512-tTDLRHaFoobRe//dz8kOJeK1pqn2eeFYDdeSYTVvTq3+HuaMc5vciH+bt7tNmlZaZ6S3L85BzI7OSgcqXl9IdQ==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-10.1.4.tgz", + "integrity": "sha512-VnGAIoluZ4OngK3y8r3oRfLHEB3aos1+acTutrc0q+aF0welkF0L+wdOjmwnLwDP1bMiQ3IKzEUoHHPHFpLBmg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/builder-vite": "10.1.2", - "@storybook/web-components": "10.1.2" + "@storybook/builder-vite": "10.1.4", + "@storybook/web-components": "10.1.4" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^10.1.2" + "storybook": "^10.1.4" } }, "node_modules/@testing-library/dom": { @@ -2816,13 +2644,6 @@ "@testing-library/dom": ">=7.21.4" } }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true, - "license": "MIT" - }, "node_modules/@ts-graphviz/adapter": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@ts-graphviz/adapter/-/adapter-2.0.6.tgz", @@ -2988,13 +2809,6 @@ "@types/qs": "*" } }, - "node_modules/@types/command-line-args": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.3.tgz", - "integrity": "sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3064,15 +2878,15 @@ "license": "MIT" }, "node_modules/@types/express": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.5.tgz", - "integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "dev": true, "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^1" + "@types/serve-static": "^2" } }, "node_modules/@types/express-serve-static-core": { @@ -3189,20 +3003,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mocha": { - "version": "10.0.10", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", - "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", @@ -3235,9 +3035,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz", - "integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", + "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "dev": true, "license": "MIT", "peer": true, @@ -3245,64 +3045,27 @@ "csstype": "^3.2.2" } }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/send": { - "version": "0.17.6", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", - "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, "node_modules/@types/serve-static": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", - "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "<1" - } - }, - "node_modules/@types/sinon": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.0.tgz", - "integrity": "sha512-+oHKZ0lTI+WVLxx1IbJDNmReQaIsQJjN2e7UUrJHEeByG7bFeKJYsv1E75JxTQ9QKJDp21bAa/0W2Xo4srsDnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/sinonjs__fake-timers": "*" - } - }, - "node_modules/@types/sinon-chai": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.12.tgz", - "integrity": "sha512-9y0Gflk3b0+NhQZ/oxGtaAJDvRywCa5sIyaVnounqLvmf93yBF4EgIRspePtkMs3Tr844nCclYMlcCNmLCvjuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "*", - "@types/sinon": "*" + "@types/node": "*" } }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-15.0.1.tgz", - "integrity": "sha512-Ko2tjWJq8oozHzHV+reuvS5KYIRAokHnGbDwGh/J64LntgpbuylF74ipEL24HCyRjf9FOlBiBHWBR1RlVKsI1w==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -3325,26 +3088,15 @@ "@types/node": "*" } }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@typescript-eslint/project-service": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz", - "integrity": "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==", + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.1.tgz", + "integrity": "sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.47.0", - "@typescript-eslint/types": "^8.47.0", + "@typescript-eslint/tsconfig-utils": "^8.48.1", + "@typescript-eslint/types": "^8.48.1", "debug": "^4.3.4" }, "engines": { @@ -3384,9 +3136,9 @@ "license": "MIT" }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz", - "integrity": "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==", + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.1.tgz", + "integrity": "sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==", "dev": true, "license": "MIT", "engines": { @@ -3401,9 +3153,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz", - "integrity": "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==", + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.1.tgz", + "integrity": "sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==", "dev": true, "license": "MIT", "engines": { @@ -3415,21 +3167,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz", - "integrity": "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==", + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.1.tgz", + "integrity": "sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.47.0", - "@typescript-eslint/tsconfig-utils": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", + "@typescript-eslint/project-service": "8.48.1", + "@typescript-eslint/tsconfig-utils": "8.48.1", + "@typescript-eslint/types": "8.48.1", + "@typescript-eslint/visitor-keys": "8.48.1", "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", + "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "engines": { @@ -3495,13 +3246,13 @@ "license": "MIT" }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz", - "integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==", + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.1.tgz", + "integrity": "sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/types": "8.48.1", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3519,6 +3270,159 @@ "license": "ISC", "peer": true }, + "node_modules/@vitest/browser": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.0.15.tgz", + "integrity": "sha512-zedtczX688KehaIaAv7m25CeDLb0gBtAOa2Oi1G1cqvSO5aLSVfH6lpZMJLW8BKYuWMxLQc9/5GYoM+jgvGIrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/mocker": "4.0.15", + "@vitest/utils": "4.0.15", + "magic-string": "^0.30.21", + "pixelmatch": "7.1.0", + "pngjs": "^7.0.0", + "sirv": "^3.0.2", + "tinyrainbow": "^3.0.3", + "ws": "^8.18.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.0.15" + } + }, + "node_modules/@vitest/browser-playwright": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.0.15.tgz", + "integrity": "sha512-94yVpDbb+ykiT7mK6ToonGnq2GIHEQGBTZTAzGxBGQXcVNCh54YKC2/WkfaDzxy0m6Kgw05kq3FYHKHu+wRdIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/browser": "4.0.15", + "@vitest/mocker": "4.0.15", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "playwright": "*", + "vitest": "4.0.15" + }, + "peerDependenciesMeta": { + "playwright": { + "optional": false + } + } + }, + "node_modules/@vitest/browser-playwright/node_modules/@vitest/mocker": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.15.tgz", + "integrity": "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.15", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/browser-playwright/node_modules/@vitest/spy": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.15.tgz", + "integrity": "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/browser/node_modules/@vitest/mocker": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.15.tgz", + "integrity": "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.15", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/browser/node_modules/@vitest/spy": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.15.tgz", + "integrity": "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.15.tgz", + "integrity": "sha512-FUJ+1RkpTFW7rQITdgTi93qOCWJobWhBirEPCeXh2SW2wsTlFxy51apDz5gzG+ZEYt/THvWeNmhdAoS9DTwpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.0.15", + "ast-v8-to-istanbul": "^0.3.8", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.1", + "obug": "^2.1.1", + "std-env": "^3.10.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.0.15", + "vitest": "4.0.15" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, "node_modules/@vitest/expect": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", @@ -3547,6 +3451,61 @@ "assertion-error": "^2.0.1" } }, + "node_modules/@vitest/expect/node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vitest/expect/node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@vitest/mocker": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", @@ -3574,24 +3533,45 @@ } } }, - "node_modules/@vitest/mocker/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "node_modules/@vitest/pretty-format": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.15.tgz", + "integrity": "sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0" + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "node_modules/@vitest/runner": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.15.tgz", + "integrity": "sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "tinyrainbow": "^2.0.0" + "@vitest/utils": "4.0.15", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.15.tgz", + "integrity": "sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/pretty-format": "4.0.15", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" @@ -3611,15 +3591,14 @@ } }, "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.15.tgz", + "integrity": "sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" + "@vitest/pretty-format": "4.0.15", + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" @@ -3633,63 +3612,77 @@ "license": "MIT" }, "node_modules/@vue/compiler-core": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", - "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz", + "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", - "@vue/shared": "3.5.24", + "@vue/shared": "3.5.25", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@vue/compiler-dom": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", - "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz", + "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.24", - "@vue/shared": "3.5.24" + "@vue/compiler-core": "3.5.25", + "@vue/shared": "3.5.25" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz", - "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==", + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz", + "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", - "@vue/compiler-core": "3.5.24", - "@vue/compiler-dom": "3.5.24", - "@vue/compiler-ssr": "3.5.24", - "@vue/shared": "3.5.24", + "@vue/compiler-core": "3.5.25", + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, + "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz", - "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==", + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz", + "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.24", - "@vue/shared": "3.5.24" + "@vue/compiler-dom": "3.5.25", + "@vue/shared": "3.5.25" } }, "node_modules/@vue/shared": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", - "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz", + "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==", "dev": true, "license": "MIT" }, @@ -3719,40 +3712,10 @@ "node": ">=10.0.0" } }, - "node_modules/@web/dev-server": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/@web/dev-server/-/dev-server-0.4.6.tgz", - "integrity": "sha512-jj/1bcElAy5EZet8m2CcUdzxT+CRvUjIXGh8Lt7vxtthkN9PzY9wlhWx/9WOs5iwlnG1oj0VGo6f/zvbPO0s9w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.11", - "@types/command-line-args": "^5.0.0", - "@web/config-loader": "^0.3.0", - "@web/dev-server-core": "^0.7.2", - "@web/dev-server-rollup": "^0.6.1", - "camelcase": "^6.2.0", - "command-line-args": "^5.1.1", - "command-line-usage": "^7.0.1", - "debounce": "^1.2.0", - "deepmerge": "^4.2.2", - "internal-ip": "^6.2.0", - "nanocolors": "^0.2.1", - "open": "^8.0.2", - "portfinder": "^1.0.32" - }, - "bin": { - "wds": "dist/bin.js", - "web-dev-server": "dist/bin.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@web/dev-server-core": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@web/dev-server-core/-/dev-server-core-0.7.5.tgz", - "integrity": "sha512-Da65zsiN6iZPMRuj4Oa6YPwvsmZmo5gtPWhW2lx3GTUf5CAEapjVpZVlUXnKPL7M7zRuk72jSsIl8lo+XpTCtw==", + "node_modules/@web/dev-server-core": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@web/dev-server-core/-/dev-server-core-0.7.5.tgz", + "integrity": "sha512-Da65zsiN6iZPMRuj4Oa6YPwvsmZmo5gtPWhW2lx3GTUf5CAEapjVpZVlUXnKPL7M7zRuk72jSsIl8lo+XpTCtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3809,49 +3772,26 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@web/dev-server-esbuild": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@web/dev-server-esbuild/-/dev-server-esbuild-1.0.4.tgz", - "integrity": "sha512-ia1LxBwwRiQBYhJ7/RtLenHyPjzle3SvTw3jOZaeGv8UGXVPOkQV8fR05caOtW/DPPZaZovNAybzRKVnNiYIZg==", + "node_modules/@web/dev-server-core/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "license": "MIT", - "dependencies": { - "@mdn/browser-compat-data": "^4.0.0", - "@web/dev-server-core": "^0.7.4", - "esbuild": "^0.25.0", - "parse5": "^6.0.1", - "ua-parser-js": "^1.0.33" - }, "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@web/dev-server-rollup": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.6.4.tgz", - "integrity": "sha512-sJZfTGCCrdku5xYnQQG51odGI092hKY9YFM0X3Z0tRY3iXKXcYRaLZrErw5KfCxr6g0JRuhe4BBhqXTA5Q2I3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/plugin-node-resolve": "^15.0.1", - "@web/dev-server-core": "^0.7.2", - "nanocolors": "^0.2.1", - "parse5": "^6.0.1", - "rollup": "^4.4.0", - "whatwg-url": "^14.0.0" + "node": ">=8.3.0" }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@web/dev-server/node_modules/@web/config-loader": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@web/config-loader/-/config-loader-0.3.3.tgz", - "integrity": "sha512-ilzeQzrPpPLWZhzFCV+4doxKDGm7oKVfdKpW9wiUNVgive34NSzCw+WzXTvjE4Jgr5CkyTDIObEmMrqQEjhT0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/@web/parse5-utils": { @@ -3868,54 +3808,6 @@ "node": ">=18.0.0" } }, - "node_modules/@web/test-runner": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.20.2.tgz", - "integrity": "sha512-zfEGYEDnS0EI8qgoWFjmtkIXhqP15W40NW3dCaKtbxj5eU0a7E53f3GV/tZGD0GlZKF8d4Fyw+AFrwOJU9Z4GA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@web/browser-logs": "^0.4.0", - "@web/config-loader": "^0.3.0", - "@web/dev-server": "^0.4.0", - "@web/test-runner-chrome": "^0.18.1", - "@web/test-runner-commands": "^0.9.0", - "@web/test-runner-core": "^0.13.0", - "@web/test-runner-mocha": "^0.9.0", - "camelcase": "^6.2.0", - "command-line-args": "^5.1.1", - "command-line-usage": "^7.0.1", - "convert-source-map": "^2.0.0", - "diff": "^5.0.0", - "globby": "^11.0.1", - "nanocolors": "^0.2.1", - "portfinder": "^1.0.32", - "source-map": "^0.7.3" - }, - "bin": { - "web-test-runner": "dist/bin.js", - "wtr": "dist/bin.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@web/test-runner-chrome": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@web/test-runner-chrome/-/test-runner-chrome-0.18.1.tgz", - "integrity": "sha512-eO6ctCaqSguGM6G3cFobGHnrEs9wlv9Juj/Akyr4XLjeEMTheNULdvOXw9Bygi+QC/ir/0snMmt+/YKnfy8rYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@web/test-runner-core": "^0.13.0", - "@web/test-runner-coverage-v8": "^0.8.0", - "chrome-launcher": "^0.15.0", - "puppeteer-core": "^24.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@web/test-runner-commands": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@web/test-runner-commands/-/test-runner-commands-0.9.0.tgz", @@ -4039,102 +3931,6 @@ "node": ">=8" } }, - "node_modules/@web/test-runner-coverage-v8": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@web/test-runner-coverage-v8/-/test-runner-coverage-v8-0.8.0.tgz", - "integrity": "sha512-PskiucYpjUtgNfR2zF2AWqWwjXL7H3WW/SnCAYmzUrtob7X9o/+BjdyZ4wKbOxWWSbJO4lEdGIDLu+8X2Xw+lA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@web/test-runner-core": "^0.13.0", - "istanbul-lib-coverage": "^3.0.0", - "lru-cache": "^8.0.4", - "picomatch": "^2.2.2", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@web/test-runner-mocha": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@web/test-runner-mocha/-/test-runner-mocha-0.9.0.tgz", - "integrity": "sha512-ZL9F6FXd0DBQvo/h/+mSfzFTSRVxzV9st/AHhpgABtUtV/AIpVE9to6+xdkpu6827kwjezdpuadPfg+PlrBWqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@web/test-runner-core": "^0.13.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@web/test-runner-playwright": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/@web/test-runner-playwright/-/test-runner-playwright-0.11.1.tgz", - "integrity": "sha512-l9tmX0LtBqMaKAApS4WshpB87A/M8sOHZyfCobSGuYqnREgz5rqQpX314yx+4fwHXLLTa5N64mTrawsYkLjliw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@web/test-runner-core": "^0.13.0", - "@web/test-runner-coverage-v8": "^0.8.0", - "playwright": "^1.53.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@web/test-runner/node_modules/@web/config-loader": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@web/config-loader/-/config-loader-0.3.3.tgz", - "integrity": "sha512-ilzeQzrPpPLWZhzFCV+4doxKDGm7oKVfdKpW9wiUNVgive34NSzCw+WzXTvjE4Jgr5CkyTDIObEmMrqQEjhT0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@web/test-runner/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@web/test-runner/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@web/test-runner/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@xn-sakina/rml-darwin-arm64": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@xn-sakina/rml-darwin-arm64/-/rml-darwin-arm64-2.6.0.tgz", @@ -4315,16 +4111,6 @@ "node": ">=0.4.0" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -4470,9 +4256,9 @@ } }, "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", "dev": true, "license": "MIT", "dependencies": { @@ -4482,6 +4268,25 @@ "node": ">=4" } }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.8.tgz", + "integrity": "sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -4493,11 +4298,14 @@ } }, "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } }, "node_modules/async-each-series": { "version": "0.1.1", @@ -4557,21 +4365,6 @@ "node": ">=4" } }, - "node_modules/b4a": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", - "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", - "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "react-native-b4a": "*" - }, - "peerDependenciesMeta": { - "react-native-b4a": { - "optional": true - } - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4579,103 +4372,6 @@ "dev": true, "license": "MIT" }, - "node_modules/bare-events": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", - "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", - "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "bare-abort-controller": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - } - } - }, - "node_modules/bare-fs": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.1.tgz", - "integrity": "sha512-zGUCsm3yv/ePt2PHNbVxjjn0nNB1MkIaR4wOCxJ2ig5pCf5cCVAYJXVhQg/3OhhJV6DB1ts7Hv0oUaElc2TPQg==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4", - "bare-url": "^2.2.2", - "fast-fifo": "^1.3.2" - }, - "engines": { - "bare": ">=1.16.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, - "node_modules/bare-os": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", - "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "engines": { - "bare": ">=1.14.0" - } - }, - "node_modules/bare-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-os": "^3.0.1" - } - }, - "node_modules/bare-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", - "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "streamx": "^2.21.0" - }, - "peerDependencies": { - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/bare-url": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", - "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-path": "^3.0.0" - } - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -4708,25 +4404,15 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.29.tgz", - "integrity": "sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==", + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -4930,16 +4616,6 @@ "dev": true, "license": "MIT/X11" }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -4965,14 +4641,14 @@ } }, "node_modules/cacheable": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.2.0.tgz", - "integrity": "sha512-LEJxRqfeomiiRd2t0uON6hxAtgOoWDfY3fugebbz+J3vDLO+SkdfFChQcOHTZhj9SYa9iwE9MGYNX72dKiOE4w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.0.tgz", + "integrity": "sha512-HHiAvOBmlcR2f3SQ7kdlYD8+AUJG+wlFZ/Ze8tl1Vzvz0MdOh8IYA/EFU4ve8t1/sZ0j4MGi7ST5MoTwHessQA==", "dev": true, "license": "MIT", "dependencies": { - "@cacheable/memory": "^2.0.5", - "@cacheable/utils": "^2.3.0", + "@cacheable/memory": "^2.0.6", + "@cacheable/utils": "^2.3.2", "hookified": "^1.13.0", "keyv": "^5.5.4", "qified": "^0.5.2" @@ -5019,23 +4695,10 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/caniuse-lite": { - "version": "1.0.30001756", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz", - "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==", + "version": "1.0.30001759", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", + "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", "dev": true, "funding": [ { @@ -5073,18 +4736,12 @@ "license": "MIT" }, "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz", + "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", "dev": true, "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, + "peer": true, "engines": { "node": ">=18" } @@ -5099,6 +4756,19 @@ "axe-core": "^4.3.3" } }, + "node_modules/chai-dom": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/chai-dom/-/chai-dom-1.12.1.tgz", + "integrity": "sha512-tvz+D0PJue2VHXRec3udgP/OeeXBiePU3VH6JhEnHQJYzvNzR2nUvEykA9dXVS76JvaUENSOYH8Ufr0kZSnlCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.12.0" + }, + "peerDependencies": { + "chai": ">= 3" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5116,22 +4786,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk-template": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", - "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/chalk-template?sponsor=1" - } - }, "node_modules/character-entities-html4": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", @@ -5186,46 +4840,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chrome-launcher": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", - "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0" - }, - "bin": { - "print-chrome-path": "bin/print-chrome-path.js" - }, - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/chromium-bidi": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-11.0.0.tgz", - "integrity": "sha512-cM3DI+OOb89T3wO8cpPSro80Q9eKYJ7hGVXoGS3GkDPxnYSqiv+6xwpIf6XERyJ9Tdsl09hmNmY94BkgZdVekw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "mitt": "^3.0.1", - "zod": "^3.24.1" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/chromium-bidi/node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" - }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -5433,32 +5047,6 @@ "node": ">=4.0.0" } }, - "node_modules/command-line-usage": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", - "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-back": "^6.2.2", - "chalk-template": "^0.4.0", - "table-layout": "^4.1.0", - "typical": "^7.1.1" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/command-line-usage/node_modules/typical": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", - "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.17" - } - }, "node_modules/commander": { "version": "14.0.2", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", @@ -5749,16 +5337,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", @@ -5803,16 +5381,6 @@ "node": ">=4.0.0" } }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", @@ -5859,21 +5427,6 @@ "node": ">=8" } }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -6131,13 +5684,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/devtools-protocol": { - "version": "0.0.1521046", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz", - "integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/didyoumean2": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/didyoumean2/-/didyoumean2-4.1.0.tgz", @@ -6153,16 +5699,6 @@ "node": ">=10.13" } }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -6241,9 +5777,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.256", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.256.tgz", - "integrity": "sha512-uqYq1IQhpXXLX+HgiXdyOZml7spy4xfy42yPxcCCRjswp0fYM2X+JwCON07lqnpLEGVCj739B7Yr+FngmHBMEQ==", + "version": "1.5.263", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", + "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", "dev": true, "license": "ISC" }, @@ -6264,16 +5800,6 @@ "node": ">= 0.8" } }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/engine.io": { "version": "6.6.4", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", @@ -6521,9 +6047,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", + "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -6534,32 +6060,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "@esbuild/aix-ppc64": "0.27.0", + "@esbuild/android-arm": "0.27.0", + "@esbuild/android-arm64": "0.27.0", + "@esbuild/android-x64": "0.27.0", + "@esbuild/darwin-arm64": "0.27.0", + "@esbuild/darwin-x64": "0.27.0", + "@esbuild/freebsd-arm64": "0.27.0", + "@esbuild/freebsd-x64": "0.27.0", + "@esbuild/linux-arm": "0.27.0", + "@esbuild/linux-arm64": "0.27.0", + "@esbuild/linux-ia32": "0.27.0", + "@esbuild/linux-loong64": "0.27.0", + "@esbuild/linux-mips64el": "0.27.0", + "@esbuild/linux-ppc64": "0.27.0", + "@esbuild/linux-riscv64": "0.27.0", + "@esbuild/linux-s390x": "0.27.0", + "@esbuild/linux-x64": "0.27.0", + "@esbuild/netbsd-arm64": "0.27.0", + "@esbuild/netbsd-x64": "0.27.0", + "@esbuild/openbsd-arm64": "0.27.0", + "@esbuild/openbsd-x64": "0.27.0", + "@esbuild/openharmony-arm64": "0.27.0", + "@esbuild/sunos-x64": "0.27.0", + "@esbuild/win32-arm64": "0.27.0", + "@esbuild/win32-ia32": "0.27.0", + "@esbuild/win32-x64": "0.27.0" } }, "node_modules/escalade": { @@ -6580,16 +6106,13 @@ "license": "MIT" }, "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "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, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, "node_modules/escodegen": { @@ -6663,11 +6186,14 @@ } }, "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } }, "node_modules/esutils": { "version": "2.0.3", @@ -6696,16 +6222,6 @@ "dev": true, "license": "MIT" }, - "node_modules/events-universal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", - "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bare-events": "^2.7.0" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -6730,68 +6246,17 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extract-zip/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, + "license": "Apache-2.0", + "peer": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12.0.0" } }, - "node_modules/extract-zip/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6806,13 +6271,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true, - "license": "MIT" - }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -6867,16 +6325,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/file-entry-cache": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.1.tgz", @@ -7192,72 +6640,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-uri": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", - "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">= 14" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/get-uri/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/get-uri/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -7412,9 +6820,9 @@ } }, "node_modules/hashery": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.2.0.tgz", - "integrity": "sha512-43XJKpwle72Ik5Zpam7MuzRWyNdwwdf6XHlh8wCj2PggvWf+v/Dm5B0dxGZOmddidgeO6Ofu9As/o231Ti/9PA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.3.0.tgz", + "integrity": "sha512-fWltioiy5zsSAs9ouEnvhsVJeAXRybGCNNv0lvzpzNOSDbULXRy7ivFWwCCv4I5Am6kSo75hmbsCduOoc2/K4w==", "dev": true, "license": "MIT", "dependencies": { @@ -7579,84 +6987,6 @@ "node": ">=8.0.0" } }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -7884,16 +7214,6 @@ "url": "https://github.com/sindresorhus/internal-ip?sponsor=1" } }, - "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, "node_modules/ip-regex": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", @@ -8048,13 +7368,6 @@ "node": ">=8" } }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true, - "license": "MIT" - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -8241,6 +7554,46 @@ "node": ">=10" } }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/istanbul-reports": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", @@ -8548,17 +7901,6 @@ "node": ">=6" } }, - "node_modules/lighthouse-logger": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", - "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^2.6.9", - "marky": "^1.2.2" - } - }, "node_modules/limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", @@ -8836,16 +8178,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lit-analyzer/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, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/lit-analyzer/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -9144,6 +8476,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.1.tgz", + "integrity": "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "source-map-js": "^1.2.1" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -9212,13 +8556,6 @@ "shiki": ">=1.0.0" } }, - "node_modules/marky": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", - "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -9592,6 +8929,16 @@ "node": ">=18" } }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -9648,16 +8995,6 @@ "node": ">= 0.6" } }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", @@ -9752,6 +9089,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -9882,34 +9230,35 @@ } }, "node_modules/oxc-resolver": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.13.2.tgz", - "integrity": "sha512-1SXVyYQ9bqMX3uZo8Px81EG7jhZkO9PvvR5X9roY5TLYVm4ZA7pbPDNlYaDBBeF9U+YO3OeMNoHde52hrcCu8w==", + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.14.2.tgz", + "integrity": "sha512-M5fERQKcrCngMZNnk1gRaBbYcqpqXLgMcoqAo7Wpty+KH0I18i03oiy2peUsGJwFaKAEbmo+CtAyhXh08RZ1RA==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.13.2", - "@oxc-resolver/binding-android-arm64": "11.13.2", - "@oxc-resolver/binding-darwin-arm64": "11.13.2", - "@oxc-resolver/binding-darwin-x64": "11.13.2", - "@oxc-resolver/binding-freebsd-x64": "11.13.2", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.13.2", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.13.2", - "@oxc-resolver/binding-linux-arm64-gnu": "11.13.2", - "@oxc-resolver/binding-linux-arm64-musl": "11.13.2", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.13.2", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.13.2", - "@oxc-resolver/binding-linux-riscv64-musl": "11.13.2", - "@oxc-resolver/binding-linux-s390x-gnu": "11.13.2", - "@oxc-resolver/binding-linux-x64-gnu": "11.13.2", - "@oxc-resolver/binding-linux-x64-musl": "11.13.2", - "@oxc-resolver/binding-wasm32-wasi": "11.13.2", - "@oxc-resolver/binding-win32-arm64-msvc": "11.13.2", - "@oxc-resolver/binding-win32-ia32-msvc": "11.13.2", - "@oxc-resolver/binding-win32-x64-msvc": "11.13.2" + "@oxc-resolver/binding-android-arm-eabi": "11.14.2", + "@oxc-resolver/binding-android-arm64": "11.14.2", + "@oxc-resolver/binding-darwin-arm64": "11.14.2", + "@oxc-resolver/binding-darwin-x64": "11.14.2", + "@oxc-resolver/binding-freebsd-x64": "11.14.2", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.14.2", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.14.2", + "@oxc-resolver/binding-linux-arm64-gnu": "11.14.2", + "@oxc-resolver/binding-linux-arm64-musl": "11.14.2", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.14.2", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.14.2", + "@oxc-resolver/binding-linux-riscv64-musl": "11.14.2", + "@oxc-resolver/binding-linux-s390x-gnu": "11.14.2", + "@oxc-resolver/binding-linux-x64-gnu": "11.14.2", + "@oxc-resolver/binding-linux-x64-musl": "11.14.2", + "@oxc-resolver/binding-openharmony-arm64": "11.14.2", + "@oxc-resolver/binding-wasm32-wasi": "11.14.2", + "@oxc-resolver/binding-win32-arm64-msvc": "11.14.2", + "@oxc-resolver/binding-win32-ia32-msvc": "11.14.2", + "@oxc-resolver/binding-win32-x64-msvc": "11.14.2" } }, "node_modules/p-event": { @@ -9951,65 +9300,6 @@ "node": ">=8" } }, - "node_modules/pac-proxy-agent": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", - "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.6", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/pac-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "dev": true, - "license": "MIT", - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -10140,6 +9430,14 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/pathval": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", @@ -10150,13 +9448,6 @@ "node": ">= 14.16" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -10190,6 +9481,19 @@ "node": ">=0.10" } }, + "node_modules/pixelmatch": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-7.1.0.tgz", + "integrity": "sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==", + "dev": true, + "license": "ISC", + "dependencies": { + "pngjs": "^7.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, "node_modules/playwright": { "version": "1.57.0", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", @@ -10247,45 +9551,16 @@ "node": ">=4" } }, - "node_modules/portfinder": { - "version": "1.0.38", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz", - "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==", + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", "dev": true, "license": "MIT", - "dependencies": { - "async": "^3.2.6", - "debug": "^4.3.6" - }, "engines": { - "node": ">= 10.12" + "node": ">=14.19.0" } }, - "node_modules/portfinder/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/portfinder/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/portscanner": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", @@ -10301,16 +9576,6 @@ "npm": ">=1.0.0" } }, - "node_modules/portscanner/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/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -10409,9 +9674,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", "dependencies": { @@ -10488,9 +9753,9 @@ } }, "node_modules/prettier": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.3.tgz", - "integrity": "sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", "bin": { @@ -10562,16 +9827,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/property-information": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", @@ -10583,89 +9838,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/proxy-agent": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.6", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.1.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/proxy-agent/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/punycode.js": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", @@ -10676,72 +9848,6 @@ "node": ">=6" } }, - "node_modules/puppeteer-core": { - "version": "24.30.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.30.0.tgz", - "integrity": "sha512-2S3Smy0t0W4wJnNvDe7W0bE7wDmZjfZ3ljfMgJd6hn2Hq/f0jgN+x9PULZo2U3fu5UUIJ+JP8cNUGllu8P91Pg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "2.10.13", - "chromium-bidi": "11.0.0", - "debug": "^4.4.3", - "devtools-protocol": "0.0.1521046", - "typed-query-selector": "^2.12.0", - "webdriver-bidi-protocol": "0.3.8", - "ws": "^8.18.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/puppeteer-core/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/puppeteer-core/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/qified": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/qified/-/qified-0.5.2.tgz", @@ -10810,42 +9916,46 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "dev": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/raw-body/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/raw-body/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, "license": "MIT", "engines": { @@ -10944,19 +10054,6 @@ "node": ">= 4" } }, - "node_modules/recast/node_modules/ast-types": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", - "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/recast/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -11029,9 +10126,9 @@ } }, "node_modules/requirejs": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz", - "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.8.tgz", + "integrity": "sha512-7/cTSLOdYkNBNJcDMWf+luFvMriVm7eYxp4BcFCsAX0wF421Vyce5SXP17c+Jd5otXKGNehIonFlyQXSowL6Mw==", "dev": true, "license": "MIT", "bin": { @@ -12117,18 +11214,18 @@ } }, "node_modules/shiki": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.18.0.tgz", - "integrity": "sha512-SDNJms7EDHQN+IC67VUQ4IzePTmeEKGZk4HvgaQ+G0fsE9Mb3R7U8zbEBjAkKZBRCJPa2ad88UzWNLLli1oNXg==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.19.0.tgz", + "integrity": "sha512-77VJr3OR/VUZzPiStyRhADmO2jApMM0V2b1qf0RpfWya8Zr1PeZev5AEpPGAAKWdiYUtcZGBE4F5QvJml1PvWA==", "license": "MIT", "peer": true, "dependencies": { - "@shikijs/core": "3.18.0", - "@shikijs/engine-javascript": "3.18.0", - "@shikijs/engine-oniguruma": "3.18.0", - "@shikijs/langs": "3.18.0", - "@shikijs/themes": "3.18.0", - "@shikijs/types": "3.18.0", + "@shikijs/core": "3.19.0", + "@shikijs/engine-javascript": "3.19.0", + "@shikijs/engine-oniguruma": "3.19.0", + "@shikijs/langs": "3.19.0", + "@shikijs/themes": "3.19.0", + "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } @@ -12209,6 +11306,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC", + "peer": true + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -12216,32 +11321,19 @@ "dev": true, "license": "ISC" }, - "node_modules/sinon": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", - "integrity": "sha512-TOgRcwFPbfGtpqvZw+hyqJDvqfapr1qUlOizROIk4bBLjlsjlB00Pg6wMFXNtJRpu+eCZuVOaLatG7M8105kAw==", + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^13.0.5", - "@sinonjs/samsam": "^8.0.1", - "diff": "^7.0.0", - "supports-color": "^7.2.0" + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "node_modules/sinon/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "license": "BSD-3-Clause", "engines": { - "node": ">=0.3.1" + "node": ">=18" } }, "node_modules/slash": { @@ -12287,17 +11379,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, "node_modules/socket.io": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", @@ -12480,61 +11561,6 @@ "dev": true, "license": "MIT" }, - "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ip-address": "^10.0.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socks-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/source-map": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", @@ -12566,6 +11592,14 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/statuses": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", @@ -12576,10 +11610,17 @@ "node": ">= 0.6" } }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/storybook": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.1.2.tgz", - "integrity": "sha512-yFL15WVQJeagmptyRadd2cwJlMVCo6xPoTPt/R+lQXIJmsTDHOFl5cZooIsvgALe3hTi5hsuVL3pG2bPEUuYGg==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.1.4.tgz", + "integrity": "sha512-FrBjm8I8O+pYEOPHcdW9xWwgXSZxte7lza9q2lN3jFN4vuW79m5j0OnTQeR8z9MmIbBTvkIpp3yMBebl53Yt5Q==", "dev": true, "license": "MIT", "dependencies": { @@ -12611,28 +11652,6 @@ } } }, - "node_modules/storybook/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/stream-throttle": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", @@ -12667,18 +11686,6 @@ "any-promise": "^1.1.0" } }, - "node_modules/streamx": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", - "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "events-universal": "^1.0.0", - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -13258,20 +12265,6 @@ "node": ">=10.0.0" } }, - "node_modules/table-layout": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", - "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-back": "^6.2.2", - "wordwrapjs": "^5.1.0" - }, - "engines": { - "node": ">=12.17" - } - }, "node_modules/table/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -13329,50 +12322,32 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/tar-fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", - "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } + "license": "MIT" }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } + "peer": true }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" } }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "dev": true, - "license": "MIT" - }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -13422,9 +12397,9 @@ } }, "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", "dev": true, "license": "MIT", "engines": { @@ -13464,17 +12439,14 @@ "node": ">=0.6" } }, - "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true, "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, "engines": { - "node": ">=18" + "node": ">=6" } }, "node_modules/tree-kill": { @@ -13596,16 +12568,6 @@ "node": ">=0.6.x" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -13633,13 +12595,6 @@ "node": ">= 0.6" } }, - "node_modules/typed-query-selector": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", - "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", - "dev": true, - "license": "MIT" - }, "node_modules/typedoc": { "version": "0.28.15", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.15.tgz", @@ -13996,21 +12951,6 @@ "node": ">= 0.4.0" } }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/varint": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", @@ -14133,115 +13073,762 @@ } } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "node": ">=18" } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=18" } }, - "node_modules/vscode-css-languageservice": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-4.3.0.tgz", - "integrity": "sha512-BkQAMz4oVHjr0oOAz5PdeE72txlLQK7NIwzmclfr+b6fj6I8POwB+VoXvrZLTbWt9hWRgfvgiQRkh5JwrjPJ5A==", + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "3.16.0-next.2", - "vscode-nls": "^4.1.2", - "vscode-uri": "^2.1.2" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" } }, - "node_modules/vscode-html-languageservice": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-3.1.0.tgz", - "integrity": "sha512-QAyRHI98bbEIBCqTzZVA0VblGU40na0txggongw5ZgTj9UVsVk5XbLT16O9OTcbqBGSqn0oWmFDNjK/XGIDcqg==", + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "3.16.0-next.2", - "vscode-nls": "^4.1.2", - "vscode-uri": "^2.1.2" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" } }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", - "dev": true, - "license": "MIT" - }, - "node_modules/vscode-languageserver-types": { - "version": "3.16.0-next.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz", - "integrity": "sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==", - "dev": true, - "license": "MIT" - }, - "node_modules/vscode-uri": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", - "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==", + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/walkdir": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", - "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==", + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/web-component-analyzer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/web-component-analyzer/-/web-component-analyzer-2.0.0.tgz", - "integrity": "sha512-UEvwfpD+XQw99sLKiH5B1T4QwpwNyWJxp59cnlRwFfhUW6JsQpw5jMeMwi7580sNou8YL3kYoS7BWLm+yJ/jVQ==", + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.15.tgz", + "integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/expect": "4.0.15", + "@vitest/mocker": "4.0.15", + "@vitest/pretty-format": "4.0.15", + "@vitest/runner": "4.0.15", + "@vitest/snapshot": "4.0.15", + "@vitest/spy": "4.0.15", + "@vitest/utils": "4.0.15", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.15", + "@vitest/browser-preview": "4.0.15", + "@vitest/browser-webdriverio": "4.0.15", + "@vitest/ui": "4.0.15", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/vitest/node_modules/@vitest/expect": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.15.tgz", + "integrity": "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.15", + "@vitest/utils": "4.0.15", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.15.tgz", + "integrity": "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/spy": "4.0.15", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@vitest/spy": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.15.tgz", + "integrity": "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vscode-css-languageservice": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-4.3.0.tgz", + "integrity": "sha512-BkQAMz4oVHjr0oOAz5PdeE72txlLQK7NIwzmclfr+b6fj6I8POwB+VoXvrZLTbWt9hWRgfvgiQRkh5JwrjPJ5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "3.16.0-next.2", + "vscode-nls": "^4.1.2", + "vscode-uri": "^2.1.2" + } + }, + "node_modules/vscode-html-languageservice": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-3.1.0.tgz", + "integrity": "sha512-QAyRHI98bbEIBCqTzZVA0VblGU40na0txggongw5ZgTj9UVsVk5XbLT16O9OTcbqBGSqn0oWmFDNjK/XGIDcqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "3.16.0-next.2", + "vscode-nls": "^4.1.2", + "vscode-uri": "^2.1.2" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.16.0-next.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz", + "integrity": "sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", + "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", + "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/walkdir": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", + "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-component-analyzer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/web-component-analyzer/-/web-component-analyzer-2.0.0.tgz", + "integrity": "sha512-UEvwfpD+XQw99sLKiH5B1T4QwpwNyWJxp59cnlRwFfhUW6JsQpw5jMeMwi7580sNou8YL3kYoS7BWLm+yJ/jVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -14269,23 +13856,6 @@ "node": ">=14.17" } }, - "node_modules/webdriver-bidi-protocol": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.8.tgz", - "integrity": "sha512-21Yi2GhGntMc671vNBCjiAeEVknXjVRoyu+k+9xOMShu+ZQfpGQwnBqbNz/Sv4GXZ6JmutlPAi2nIJcrymAWuQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", @@ -14293,20 +13863,6 @@ "dev": true, "license": "MIT" }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -14323,14 +13879,22 @@ "node": ">= 8" } }, - "node_modules/wordwrapjs": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", - "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", + "peer": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, "engines": { - "node": ">=12.17" + "node": ">=8" } }, "node_modules/wrap-ansi": { @@ -14453,17 +14017,17 @@ } }, "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -14494,9 +14058,9 @@ } }, "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "dev": true, "license": "ISC", "bin": { @@ -14504,6 +14068,9 @@ }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yargs": { @@ -14560,17 +14127,6 @@ "node": ">=8" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/ylru": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz", @@ -14581,16 +14137,6 @@ "node": ">= 4.0.0" } }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index f8824fa45..8de7788e0 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "url": "https://github.com/IgniteUI/igniteui-webcomponents/issues" }, "scripts": { + "vitest:debug": "vitest --no-file-parallelism --isolate false", "start": "npm run storybook", "build:publish": "npm run cem && node scripts/build.mjs", "cem": "cem analyze --config cem.config.mjs", @@ -40,8 +41,9 @@ "lint:styles": "stylelint \"src/**/*.scss\"", "format": "biome check --fix && prettier \"**/*.ts\" --write --ignore-path .gitignore", "release": "node scripts/gen-changelog.mjs", - "test": "npm run build:styles && wtr --coverage", - "test:watch": "npm run build:styles && concurrently -k -r \"npm:watch-scss\" \"wtr --watch\"", + "test": "npm run build:styles && vitest --watch false --coverage", + "test:watch": "npm run build:styles && concurrently -k -r \"npm:watch-scss\" \"vitest\"", + "test:debug": "npm run build:styles && concurrently -k -r \"npm:watch-scss\" \"npm:vitest:debug\"", "storybook": "npm run build:styles && concurrently -k -r \"npm run cem:watch\" \"npm:watch-scss\" \"npm:watch-meta\" \"storybook dev -p 8000\"", "storybook:build": "npm run build:styles && storybook build -o ./storybook-static", "build:typedoc:export": "node scripts/build-typedoc.js export", @@ -63,18 +65,19 @@ "@biomejs/biome": "~2.3.8", "@custom-elements-manifest/analyzer": "^0.11.0", "@igniteui/material-icons-extended": "^3.1.0", - "@open-wc/testing": "^4.0.0", + "@open-wc/semantic-dom-diff": "^0.20.1", "@storybook/addon-a11y": "^10.1.2", "@storybook/addon-docs": "^10.1.2", "@storybook/addon-links": "^10.1.2", "@storybook/web-components-vite": "^10.1.2", - "@types/mocha": "^10.0.10", - "@web/dev-server-esbuild": "^1.0.4", - "@web/test-runner": "^0.20.2", - "@web/test-runner-playwright": "^0.11.1", + "@types/chai-dom": "^1.11.3", + "@vitest/browser-playwright": "^4.0.14", + "@vitest/coverage-v8": "^4.0.15", "autoprefixer": "^10.4.22", "browser-sync": "^3.0.4", "cem-plugin-expanded-types": "^1.4.0", + "chai-a11y-axe": "^1.5.0", + "chai-dom": "^1.12.1", "concurrently": "^9.2.1", "custom-element-jet-brains-integration": "^1.7.0", "custom-element-vs-code-integration": "^1.5.0", @@ -93,7 +96,6 @@ "prettier": "^3.7.3", "rimraf": "^6.1.2", "sass-embedded": "~1.93.3", - "sinon": "^21.0.0", "storybook": "^10.1.2", "stylelint": "^16.26.1", "stylelint-config-standard-scss": "^16.0.0", diff --git a/scripts/build.mjs b/scripts/build.mjs index 371d14d00..b8dc6634c 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -11,7 +11,6 @@ import { import customElements from '../custom-elements.json' with { type: 'json' }; import report from './report.mjs'; import { buildComponents, buildThemes } from './sass.mjs'; -import { globby } from 'globby'; const exec = promisify(_exec); @@ -51,20 +50,9 @@ async function runTask(tag, cmd) { ); await runTask('Copying release files', async () => { - const pngFiles = await globby('src/components/**/*.png'); - pngFiles.forEach( - async (dest) => - await mkdir(path.dirname(DEST_DIR(dest.replace('src', ''))), { - recursive: true, - }) - ); - Promise.all([ copyFile('scripts/_package.json', DEST_DIR('package.json')), ...RELEASE_FILES.map((file) => copyFile(file, DEST_DIR(file))), - ...pngFiles.map((file) => - copyFile(file, DEST_DIR(file.replace('src', ''))) - ), ]); }); diff --git a/scripts/tsconfig.prod.json b/scripts/tsconfig.prod.json index 597e1689a..a1c0bf3d5 100644 --- a/scripts/tsconfig.prod.json +++ b/scripts/tsconfig.prod.json @@ -2,6 +2,7 @@ "extends": "../tsconfig.json", "exclude": [ "../vite.config.ts", + "../vitest.setup.ts", "../**/*.spec.ts", "../stories" ], @@ -12,7 +13,5 @@ "declarationMap": false, "removeComments": true }, - "include": [ - "../**/*.ts" - ] + "include": ["../**/*.ts"] } diff --git a/src/animations/player.spec.ts b/src/animations/player.spec.ts index 0760dc56f..15f1aca20 100644 --- a/src/animations/player.spec.ts +++ b/src/animations/player.spec.ts @@ -1,11 +1,11 @@ +import { css, LitElement } from 'lit'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { defineCE, - expect, fixture, html, unsafeStatic, -} from '@open-wc/testing'; -import { css, LitElement } from 'lit'; +} from '../components/common/helpers.spec.js'; import { EaseOut } from './easings.js'; import { addAnimationController } from './player.js'; @@ -27,7 +27,7 @@ describe('Animations Player', () => { let tag: string; let el: HTMLElement & { player: ReturnType }; - before(() => { + beforeAll(() => { tag = defineCE( class extends LitElement { public static override styles = css` diff --git a/src/components/accordion/accordion.spec.ts b/src/components/accordion/accordion.spec.ts index c2bd48bc5..ab658bf2c 100644 --- a/src/components/accordion/accordion.spec.ts +++ b/src/components/accordion/accordion.spec.ts @@ -1,5 +1,4 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; - +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { altKey, arrowDown, @@ -9,12 +8,13 @@ import { shiftKey, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { simulateClick, simulateKeyboard } from '../common/utils.spec.js'; import IgcExpansionPanelComponent from '../expansion-panel/expansion-panel.js'; import IgcAccordionComponent from './accordion.js'; describe('Accordion', () => { - before(() => { + beforeAll(() => { defineComponents(IgcAccordionComponent); }); diff --git a/src/components/avatar/avatar.spec.ts b/src/components/avatar/avatar.spec.ts index 44d81bdad..21bfdd163 100644 --- a/src/components/avatar/avatar.spec.ts +++ b/src/components/avatar/avatar.spec.ts @@ -1,6 +1,6 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; - +import { beforeAll, describe, expect, it } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import IgcAvatarComponent from './avatar.js'; describe('Avatar', () => { @@ -8,7 +8,7 @@ describe('Avatar', () => { ignoreAttributes: ['style'], }; - before(() => { + beforeAll(() => { defineComponents(IgcAvatarComponent); }); diff --git a/src/components/badge/badge.spec.ts b/src/components/badge/badge.spec.ts index 81a8fd291..fd4fe228c 100644 --- a/src/components/badge/badge.spec.ts +++ b/src/components/badge/badge.spec.ts @@ -1,10 +1,10 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; - +import { beforeAll, describe, expect, it } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import IgcBadgeComponent from './badge.js'; describe('Badge', () => { - before(() => { + beforeAll(() => { defineComponents(IgcBadgeComponent); }); diff --git a/src/components/badge/badge.ts b/src/components/badge/badge.ts index 9e274d354..2965728d8 100644 --- a/src/components/badge/badge.ts +++ b/src/components/badge/badge.ts @@ -1,8 +1,8 @@ import { html, LitElement, type PropertyValues } from 'lit'; -import { property } from 'lit/decorators.js'; +import { property, state } from 'lit/decorators.js'; import { addThemingController } from '../../theming/theming-controller.js'; import { addInternalsController } from '../common/controllers/internals.js'; -import { addSlotController } from '../common/controllers/slot.js'; +import { addSlotController, setSlots } from '../common/controllers/slot.js'; import { registerComponent } from '../common/definitions/register.js'; import { partMap } from '../common/part-map.js'; import type { BadgeShape, StyleVariant } from '../types.js'; @@ -29,24 +29,18 @@ export default class IgcBadgeComponent extends LitElement { registerComponent(IgcBadgeComponent); } - private _iconPart = false; + private readonly _internals = addInternalsController(this, { + initialARIA: { role: 'status' }, + }); private readonly _slots = addSlotController(this, { + slots: setSlots(), onChange: this._handleSlotChange, + initial: true, }); - protected _handleSlotChange(): void { - const assignedNodes = this._slots.getAssignedNodes('[default]', true); - this._iconPart = assignedNodes.some( - (node) => - node.nodeType === Node.ELEMENT_NODE && - (node as Element).tagName.toLowerCase() === 'igc-icon' - ); - } - - private readonly _internals = addInternalsController(this, { - initialARIA: { role: 'status' }, - }); + @state() + private _iconPart = false; /** * The type of badge. @@ -87,6 +81,13 @@ export default class IgcBadgeComponent extends LitElement { } } + protected _handleSlotChange(): void { + this._iconPart = this._slots.hasAssignedElements('[default]', { + flatten: true, + selector: 'igc-icon', + }); + } + protected override render() { return html` diff --git a/src/components/banner/banner.spec.ts b/src/components/banner/banner.spec.ts index f786930da..faeae8f71 100644 --- a/src/components/banner/banner.spec.ts +++ b/src/components/banner/banner.spec.ts @@ -1,19 +1,17 @@ +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, nextFrame, -} from '@open-wc/testing'; -import { spy } from 'sinon'; - -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { finishAnimationsFor, simulateClick } from '../common/utils.spec.js'; import IgcIconComponent from '../icon/icon.js'; import IgcBannerComponent from './banner.js'; describe('Banner', () => { - before(() => { + beforeAll(() => { defineComponents(IgcBannerComponent, IgcIconComponent); }); @@ -246,7 +244,7 @@ describe('Banner', () => { }); it('should emit correct event sequence for the default action button', async () => { - const eventSpy = spy(banner, 'emitEvent'); + const spy = vi.spyOn(banner, 'emitEvent'); const button = banner.renderRoot.querySelector('igc-button')!; expect(banner.open).to.be.false; @@ -256,19 +254,19 @@ describe('Banner', () => { simulateClick(button); - expect(eventSpy.callCount).to.equal(1); - expect(eventSpy).calledWith('igcClosing', { cancelable: true }); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith('igcClosing', { cancelable: true }); - eventSpy.resetHistory(); + spy.mockClear(); await elementUpdated(banner); await clickHideComplete(); - expect(eventSpy).calledWith('igcClosed'); + expect(spy).toHaveBeenCalledWith('igcClosed'); expect(banner.open).to.be.false; }); it('can cancel `igcClosing` event', async () => { - const eventSpy = spy(banner, 'emitEvent'); + const spy = vi.spyOn(banner, 'emitEvent'); const button = banner.renderRoot.querySelector('igc-button')!; banner.addEventListener('igcClosing', (event) => { @@ -282,8 +280,8 @@ describe('Banner', () => { await elementUpdated(banner); await clickHideComplete(); - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).not.calledWith('igcClosed'); + expect(spy).toHaveBeenCalledWith('igcClosing', { cancelable: true }); + expect(spy).not.toHaveBeenCalledWith('igcClosed'); expect(banner.open).to.be.true; }); }); diff --git a/src/components/button-group/button-group.spec.ts b/src/components/button-group/button-group.spec.ts index 77cff5fa2..efe1033b7 100644 --- a/src/components/button-group/button-group.spec.ts +++ b/src/components/button-group/button-group.spec.ts @@ -1,20 +1,16 @@ +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, unsafeStatic, -} from '@open-wc/testing'; -import { spy } from 'sinon'; - -import { - defineComponents, - IgcButtonGroupComponent, - IgcToggleButtonComponent, -} from '../../index.js'; +} from '../common/helpers.spec.js'; +import IgcButtonGroupComponent from './button-group.js'; +import IgcToggleButtonComponent from './toggle-button.js'; describe('Button Group', () => { - before(() => { + beforeAll(() => { defineComponents(IgcButtonGroupComponent); }); @@ -600,14 +596,14 @@ describe('Button Group', () => { }); it('should emit `igcSelect` event on select', async () => { - const eventSpy = spy(buttonGroup, 'emitEvent'); + const spy = vi.spyOn(buttonGroup, 'emitEvent'); buttons[0].dispatchEvent(new MouseEvent('click', { bubbles: true })); await elementUpdated(buttonGroup); const args = { detail: buttons[0].value }; - expect(eventSpy).calledWith('igcSelect', args); + expect(spy).toHaveBeenCalledWith('igcSelect', args); buttonGroup.addEventListener('igcSelect', (event) => { expect(buttonGroup.selectedItems.length).to.equal(1); @@ -618,7 +614,7 @@ describe('Button Group', () => { }); it('should emit `igcDeselect` event on deselect', async () => { - const eventSpy = spy(buttonGroup, 'emitEvent'); + const spy = vi.spyOn(buttonGroup, 'emitEvent'); buttons[0].selected = true; await elementUpdated(buttonGroup); @@ -629,7 +625,7 @@ describe('Button Group', () => { const args = { detail: buttons[0].value }; - expect(eventSpy).calledWith('igcDeselect', args); + expect(spy).toHaveBeenCalledWith('igcDeselect', args); buttonGroup.addEventListener('igcDeselect', () => { expect(buttonGroup.selectedItems.length).to.equal(0); @@ -637,7 +633,7 @@ describe('Button Group', () => { }); it('events are correctly emitted on user interaction (single mode)', async () => { - const eventSpy = spy(buttonGroup, 'emitEvent'); + const spy = vi.spyOn(buttonGroup, 'emitEvent'); const selectArgs = { detail: '' }; const deselectArgs = { detail: '' }; @@ -647,7 +643,7 @@ describe('Button Group', () => { selectArgs.detail = buttons[0].value; - expect(eventSpy).calledWith('igcSelect', selectArgs); + expect(spy).toHaveBeenCalledWith('igcSelect', selectArgs); expect(buttonGroup.selectedItems.length).to.equal(1); expect(buttonGroup.selectedItems).to.have.same.members([ buttons[0].value, @@ -660,8 +656,8 @@ describe('Button Group', () => { selectArgs.detail = buttons[1].value; deselectArgs.detail = buttons[0].value; - expect(eventSpy).calledWith('igcDeselect', deselectArgs); - expect(eventSpy).calledWith('igcSelect', selectArgs); + expect(spy).toHaveBeenCalledWith('igcDeselect', deselectArgs); + expect(spy).toHaveBeenCalledWith('igcSelect', selectArgs); expect(buttonGroup.selectedItems.length).to.equal(1); expect(buttonGroup.selectedItems).to.have.same.members([ buttons[1].value, @@ -669,7 +665,7 @@ describe('Button Group', () => { }); it('events are correctly emitted on user interaction (single-required mode)', async () => { - const eventSpy = spy(buttonGroup, 'emitEvent'); + const spy = vi.spyOn(buttonGroup, 'emitEvent'); const selectArgs = { detail: '' }; const deselectArgs = { detail: '' }; @@ -682,21 +678,21 @@ describe('Button Group', () => { selectArgs.detail = buttons[0].value; - expect(eventSpy).calledWith('igcSelect', selectArgs); + expect(spy).toHaveBeenCalledWith('igcSelect', selectArgs); expect(buttonGroup.selectedItems.length).to.equal(1); expect(buttonGroup.selectedItems).to.have.same.members([ buttons[0].value, ]); - eventSpy.resetHistory(); + spy.mockClear(); // deselect first button // should not emit events when interacting with an already selected button buttons[0].dispatchEvent(new MouseEvent('click', { bubbles: true })); await elementUpdated(buttonGroup); - expect(eventSpy).not.calledWith('igcDeselect'); - expect(eventSpy).not.calledWith('igcSelect'); + expect(spy).not.toHaveBeenCalledWith('igcDeselect'); + expect(spy).not.toHaveBeenCalledWith('igcSelect'); expect(buttonGroup.selectedItems.length).to.equal(1); expect(buttonGroup.selectedItems).to.have.same.members([ buttons[0].value, @@ -709,8 +705,8 @@ describe('Button Group', () => { selectArgs.detail = buttons[1].value; deselectArgs.detail = buttons[0].value; - expect(eventSpy).calledWith('igcDeselect', deselectArgs); - expect(eventSpy).calledWith('igcSelect', selectArgs); + expect(spy).toHaveBeenCalledWith('igcDeselect', deselectArgs); + expect(spy).toHaveBeenCalledWith('igcSelect', selectArgs); expect(buttonGroup.selectedItems.length).to.equal(1); expect(buttonGroup.selectedItems).to.have.same.members([ buttons[1].value, @@ -718,7 +714,7 @@ describe('Button Group', () => { }); it('events are correctly emitted on user interaction (multiple mode)', async () => { - const eventSpy = spy(buttonGroup, 'emitEvent'); + const spy = vi.spyOn(buttonGroup, 'emitEvent'); const selectArgs = { detail: '' }; const deselectArgs = { detail: '' }; @@ -731,7 +727,7 @@ describe('Button Group', () => { selectArgs.detail = buttons[0].value; - expect(eventSpy).calledWith('igcSelect', selectArgs); + expect(spy).toHaveBeenCalledWith('igcSelect', selectArgs); expect(buttonGroup.selectedItems.length).to.equal(1); expect(buttonGroup.selectedItems).to.have.same.members([ buttons[0].value, @@ -743,8 +739,8 @@ describe('Button Group', () => { selectArgs.detail = buttons[1].value; - expect(eventSpy).not.calledWith('igcDeselect'); - expect(eventSpy).calledWith('igcSelect', selectArgs); + expect(spy).not.toHaveBeenCalledWith('igcDeselect'); + expect(spy).toHaveBeenCalledWith('igcSelect', selectArgs); expect(buttonGroup.selectedItems.length).to.equal(2); expect(buttonGroup.selectedItems).to.have.same.members([ buttons[0].value, @@ -757,7 +753,7 @@ describe('Button Group', () => { deselectArgs.detail = buttons[0].value; - expect(eventSpy).calledWith('igcDeselect', deselectArgs); + expect(spy).toHaveBeenCalledWith('igcDeselect', deselectArgs); expect(buttonGroup.selectedItems.length).to.equal(1); expect(buttonGroup.selectedItems).to.have.same.members([ buttons[1].value, diff --git a/src/components/button-group/toggle-button.spec.ts b/src/components/button-group/toggle-button.spec.ts index 7581a6a1e..7aff22552 100644 --- a/src/components/button-group/toggle-button.spec.ts +++ b/src/components/button-group/toggle-button.spec.ts @@ -1,15 +1,15 @@ +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, unsafeStatic, -} from '@open-wc/testing'; - -import { defineComponents, IgcToggleButtonComponent } from '../../index.js'; +} from '../common/helpers.spec.js'; +import IgcToggleButtonComponent from './toggle-button.js'; describe('Toggle Button', () => { - before(() => { + beforeAll(() => { defineComponents(IgcToggleButtonComponent); }); diff --git a/src/components/button/button.spec.ts b/src/components/button/button.spec.ts index 590d8cf09..e43739164 100644 --- a/src/components/button/button.spec.ts +++ b/src/components/button/button.spec.ts @@ -1,6 +1,6 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; - +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, isFocused, @@ -18,7 +18,9 @@ const Types: Array = ['button', 'reset', 'submit']; describe('Button tests', () => { let button: IgcButtonComponent; - before(() => defineComponents(IgcButtonComponent, IgcInputComponent)); + beforeAll(() => { + defineComponents(IgcButtonComponent, IgcInputComponent); + }); describe('Button component', () => { const ignored_DOM_parts = { diff --git a/src/components/button/icon-button.spec.ts b/src/components/button/icon-button.spec.ts index 13c89d334..a83828c0a 100644 --- a/src/components/button/icon-button.spec.ts +++ b/src/components/button/icon-button.spec.ts @@ -1,15 +1,14 @@ +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import { defineComponents, IgcIconButtonComponent } from '../../index.js'; import { elementUpdated, - expect, fixture, html, unsafeStatic, -} from '@open-wc/testing'; - -import { defineComponents, IgcIconButtonComponent } from '../../index.js'; +} from '../common/helpers.spec.js'; describe('IconButton component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcIconButtonComponent); }); diff --git a/src/components/calendar/calendar-keyboard-navigation.spec.ts b/src/components/calendar/calendar-keyboard-navigation.spec.ts index 4e8aa4a86..d1c1cd763 100644 --- a/src/components/calendar/calendar-keyboard-navigation.spec.ts +++ b/src/components/calendar/calendar-keyboard-navigation.spec.ts @@ -1,5 +1,4 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; - +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { arrowDown, arrowLeft, @@ -14,6 +13,7 @@ import { spaceBar, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { asNumber, first } from '../common/util.js'; import { simulateClick, simulateKeyboard } from '../common/utils.spec.js'; import IgcCalendarComponent from './calendar.js'; @@ -26,7 +26,7 @@ import { DateRangeType } from './types.js'; import type IgcYearsViewComponent from './years-view/years-view.js'; describe('Calendar keyboard interaction', () => { - before(() => defineComponents(IgcCalendarComponent)); + beforeAll(() => defineComponents(IgcCalendarComponent)); let calendar: IgcCalendarComponent; const activeDate = new Date(2021, 6, 17); diff --git a/src/components/calendar/calendar-rendering.spec.ts b/src/components/calendar/calendar-rendering.spec.ts index ef7ee1ca3..7db0ccba2 100644 --- a/src/components/calendar/calendar-rendering.spec.ts +++ b/src/components/calendar/calendar-rendering.spec.ts @@ -1,7 +1,7 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; import type { TemplateResult } from 'lit'; - +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { first } from '../common/util.js'; import IgcCalendarComponent from './calendar.js'; import { getCalendarDOM, getDayViewDOM, getDOMDate } from './helpers.spec.js'; @@ -9,7 +9,7 @@ import { CalendarDay } from './model.js'; import { type DateRangeDescriptor, DateRangeType } from './types.js'; describe('Calendar Rendering', () => { - before(() => { + beforeAll(() => { defineComponents(IgcCalendarComponent); }); diff --git a/src/components/calendar/calendar.interaction.spec.ts b/src/components/calendar/calendar.interaction.spec.ts index 308ac5674..a5cd65d33 100644 --- a/src/components/calendar/calendar.interaction.spec.ts +++ b/src/components/calendar/calendar.interaction.spec.ts @@ -1,9 +1,9 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import { spy } from 'sinon'; - -import { defineComponents, IgcCalendarComponent } from '../../index.js'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { first, last } from '../common/util.js'; import { simulateClick } from '../common/utils.spec.js'; +import IgcCalendarComponent from './calendar.js'; import type IgcDaysViewComponent from './days-view/days-view.js'; import { calendarRange, @@ -19,7 +19,7 @@ describe('Calendar interactions', () => { let calendar: IgcCalendarComponent; let daysView: IgcDaysViewComponent; - before(() => defineComponents(IgcCalendarComponent)); + beforeAll(() => defineComponents(IgcCalendarComponent)); beforeEach(async () => { const value = new CalendarDay({ year: 2021, month: 8, date: 29 }); @@ -177,7 +177,7 @@ describe('Calendar interactions', () => { }); it('single selection', async () => { - const eventSpy = spy(calendar, 'emitEvent'); + const spy = vi.spyOn(calendar, 'emitEvent'); const current = CalendarDay.from(calendar.value!); const previous = current.add('day', -1); @@ -187,7 +187,7 @@ describe('Calendar interactions', () => { simulateClick(previousDOM); await elementUpdated(calendar); - expect(eventSpy).calledOnceWithExactly('igcChange', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { detail: previous.native, }); @@ -196,7 +196,7 @@ describe('Calendar interactions', () => { }); it('issue-1443', async () => { - const eventSpy = spy(calendar, 'emitEvent'); + const spy = vi.spyOn(calendar, 'emitEvent'); const anchor = new CalendarDay({ year: 681, month: 0, @@ -214,7 +214,7 @@ describe('Calendar interactions', () => { simulateClick(previousDOM); await elementUpdated(calendar); - expect(eventSpy).calledOnceWithExactly('igcChange', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { detail: previous.native, }); @@ -235,7 +235,7 @@ describe('Calendar interactions', () => { }); it('multiple selection', async () => { - const eventSpy = spy(calendar, 'emitEvent'); + const spy = vi.spyOn(calendar, 'emitEvent'); const start = CalendarDay.from(calendar.value!).set({ date: 15 }); const dates = Array.from(calendarRange({ start, end: 7 })); const elements = dates.map((date) => getDOMDate(date, daysView)); @@ -247,12 +247,14 @@ describe('Calendar interactions', () => { for (const element of elements) { simulateClick(element); - expect(eventSpy).calledWith('igcChange', { detail: calendar.values }); + expect(spy).toHaveBeenCalledWith('igcChange', { + detail: calendar.values, + }); } await elementUpdated(calendar); expect(calendar.values).lengthOf(7); - expect(eventSpy.callCount).equal(7); + expect(spy).toHaveBeenCalledTimes(7); expect(first(dates).equalTo(first(calendar.values))).to.be.true; expect(last(dates).equalTo(last(calendar.values))).to.be.true; @@ -305,23 +307,25 @@ describe('Calendar interactions', () => { } }); - it('should emit `igcACtiveDateChange` event when the active date is selected', async () => { - const eventSpy = spy(daysView, 'emitEvent'); + it('should emit `igcActiveDateChange` event when the active date is selected', async () => { + const spy = vi.spyOn(daysView, 'emitEvent'); const date = CalendarDay.from(calendar.value!).set({ date: 28 }); const element = getDOMDate(date, daysView); simulateClick(element); await elementUpdated(calendar); - const argDate = CalendarDay.from(eventSpy.getCall(1).lastArg.detail); + const argDate = CalendarDay.from(spy.mock.lastCall?.[1]?.detail as Date); - expect(eventSpy).calledWith('igcActiveDateChange'); + expect(spy).toBeCalledWith('igcActiveDateChange', { + detail: argDate.native, + }); expect(argDate.equalTo(date)).to.be.true; expect(argDate.equalTo(calendar.activeDate)).to.be.true; }); it('should emit `igcRangePreviewDateChange` event', async () => { - const eventSpy = spy(daysView, 'emitEvent'); + const spy = vi.spyOn(daysView, 'emitEvent'); const start = CalendarDay.from(calendar.value!).set({ date: 26 }); const dates = Array.from(calendarRange({ start, end: 7 })); const elements = dates.map((date) => getDOMDate(date, daysView)); @@ -333,7 +337,7 @@ describe('Calendar interactions', () => { last(elements).focus(); await elementUpdated(calendar); - expect(eventSpy).calledWithExactly('igcRangePreviewDateChange', { + expect(spy).toHaveBeenCalledWith('igcRangePreviewDateChange', { detail: last(dates).native, }); }); diff --git a/src/components/calendar/model.spec.ts b/src/components/calendar/model.spec.ts index 2142eebfd..95908dece 100644 --- a/src/components/calendar/model.spec.ts +++ b/src/components/calendar/model.spec.ts @@ -1,5 +1,4 @@ -import { expect } from '@open-wc/testing'; - +import { describe, expect, it } from 'vitest'; import { first, last } from '../common/util.js'; import { calendarRange, isDateInRanges } from './helpers.js'; import { CalendarDay } from './model.js'; diff --git a/src/components/card/card.spec.ts b/src/components/card/card.spec.ts index 4c4eea13c..e29f93163 100644 --- a/src/components/card/card.spec.ts +++ b/src/components/card/card.spec.ts @@ -1,9 +1,10 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; - -import { defineComponents, IgcCardComponent } from '../../index.js'; +import { beforeAll, describe, expect, it } from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; +import IgcCardComponent from './card.js'; describe('Card Component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcCardComponent); }); diff --git a/src/components/carousel/carousel-indicator-container.spec.ts b/src/components/carousel/carousel-indicator-container.spec.ts index 4294664c0..857d19ebd 100644 --- a/src/components/carousel/carousel-indicator-container.spec.ts +++ b/src/components/carousel/carousel-indicator-container.spec.ts @@ -1,7 +1,7 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; - +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { tabKey } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { first } from '../common/util.js'; import { simulateClick, @@ -13,7 +13,7 @@ import IgcCarouselIndicatorComponent from './carousel-indicator.js'; import IgcCarouselIndicatorContainerComponent from './carousel-indicator-container.js'; describe('Carousel Indicator Container', () => { - before(() => { + beforeAll(() => { defineComponents( IgcCarouselIndicatorContainerComponent, IgcCarouselIndicatorComponent diff --git a/src/components/carousel/carousel.spec.ts b/src/components/carousel/carousel.spec.ts index 0eb28c214..44ab51811 100644 --- a/src/components/carousel/carousel.spec.ts +++ b/src/components/carousel/carousel.spec.ts @@ -1,13 +1,12 @@ import { - elementUpdated, + afterEach, + beforeAll, + beforeEach, + describe, expect, - fixture, - html, - nextFrame, - waitUntil, -} from '@open-wc/testing'; - -import { type SinonFakeTimers, spy, stub, useFakeTimers } from 'sinon'; + it, + vi, +} from 'vitest'; import IgcButtonComponent from '../button/button.js'; import { arrowLeft, @@ -19,7 +18,17 @@ import { } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; import { + elementUpdated, + fixture, + html, + nextFrame, + waitUntil, +} from '../common/helpers.spec.js'; +import { + eventArgsMatch, + eventMatch, finishAnimationsFor, + getEvents, simulateClick, simulateKeyboard, simulateLostPointerCapture, @@ -31,7 +40,7 @@ import IgcCarouselIndicatorComponent from './carousel-indicator.js'; import IgcCarouselSlideComponent from './carousel-slide.js'; describe('Carousel', () => { - before(() => { + beforeAll(() => { defineComponents(IgcCarouselComponent); }); @@ -70,7 +79,6 @@ describe('Carousel', () => { let nextButton: IgcButtonComponent; let prevButton: IgcButtonComponent; let defaultIndicators: IgcCarouselIndicatorComponent[]; - let clock: SinonFakeTimers; beforeEach(async () => { carousel = await fixture(createCarouselComponent()); @@ -494,71 +502,69 @@ describe('Carousel', () => { describe('Interactions', () => { describe('Click', () => { it('should change slide when clicking next button', async () => { - const eventSpy = spy(carousel, 'emitEvent'); + const spy = vi.spyOn(carousel, 'emitEvent'); expect(carousel.current).to.equal(0); expect(defaultIndicators[0].active).to.be.true; simulateClick(nextButton!); - await waitUntil(() => eventSpy.calledWith('igcSlideChanged')); + await waitUntil(() => eventMatch(spy, 'igcSlideChanged')); expect(carousel.current).to.equal(1); expect(defaultIndicators[0].active).to.be.false; expect(defaultIndicators[1].active).to.be.true; - expect(eventSpy.firstCall).calledWith('igcSlideChanged', { detail: 1 }); + expect(spy).toHaveBeenCalledWith('igcSlideChanged', { detail: 1 }); }); it('should change slide when clicking previous button', async () => { - const eventSpy = spy(carousel, 'emitEvent'); + const spy = vi.spyOn(carousel, 'emitEvent'); expect(carousel.current).to.equal(0); expect(defaultIndicators[0].active).to.be.true; simulateClick(prevButton!); - await waitUntil(() => eventSpy.calledWith('igcSlideChanged')); + await waitUntil(() => eventMatch(spy, 'igcSlideChanged')); expect(carousel.current).to.equal(2); expect(defaultIndicators[0].active).to.be.false; expect(defaultIndicators[2].active).to.be.true; - expect(eventSpy.firstCall).calledWith('igcSlideChanged', { detail: 2 }); + expect(spy).toHaveBeenCalledWith('igcSlideChanged', { detail: 2 }); }); it('should change slide when clicking indicators', async () => { - const eventSpy = spy(carousel, 'emitEvent'); + const spy = vi.spyOn(carousel, 'emitEvent'); expect(carousel.current).to.equal(0); expect(defaultIndicators[0].active).to.be.true; // select second slide simulateClick(defaultIndicators[1]); await waitUntil(() => - eventSpy.calledWith('igcSlideChanged', { detail: 1 }) + eventArgsMatch(spy, 'igcSlideChanged', { detail: 1 }) ); expect(carousel.current).to.equal(1); expect(defaultIndicators[0].active).to.be.false; expect(defaultIndicators[1].active).to.be.true; - expect(eventSpy.firstCall).calledWith('igcSlideChanged', { detail: 1 }); + expect(spy).toHaveBeenCalledWith('igcSlideChanged', { detail: 1 }); // select first slide simulateClick(defaultIndicators[0]); await waitUntil(() => - eventSpy.calledWith('igcSlideChanged', { detail: 0 }) + eventArgsMatch(spy, 'igcSlideChanged', { detail: 0 }) ); expect(carousel.current).to.equal(0); expect(defaultIndicators[0].active).to.be.true; expect(defaultIndicators[1].active).to.be.false; - expect(eventSpy.secondCall).calledWith('igcSlideChanged', { + expect(spy).toHaveBeenCalledWith('igcSlideChanged', { detail: 0, }); }); it('should properly call `igcSlideChanged` event', async () => { - const eventSpy = spy(carousel, 'emitEvent'); + const spy = vi.spyOn(carousel, 'emitEvent'); - stub(carousel, 'select') - .onFirstCall() - .resolves(true) - .onSecondCall() - .resolves(false); + vi.spyOn(carousel, 'select') + .mockResolvedValueOnce(true) + .mockResolvedValueOnce(false); // select second indicator simulateClick(defaultIndicators[1]); @@ -568,7 +574,7 @@ describe('Carousel', () => { simulateClick(defaultIndicators[1]); await slideChangeComplete(slides[0], slides[1]); - expect(eventSpy.callCount).to.equal(1); + expect(getEvents(spy, 'igcSlideChanged').length).to.equal(1); }); }); @@ -658,10 +664,10 @@ describe('Carousel', () => { describe('Automatic rotation', () => { beforeEach(async () => { - clock = useFakeTimers({ toFake: ['setInterval'] }); + vi.useFakeTimers({ toFake: ['setInterval', 'clearInterval'] }); }); - afterEach(() => clock.restore()); + afterEach(() => vi.useRealTimers()); it('should automatically change slides', async () => { expect(carousel.current).to.equal(0); @@ -669,14 +675,14 @@ describe('Carousel', () => { carousel.interval = 200; await elementUpdated(carousel); - await clock.tickAsync(200); + await vi.advanceTimersByTimeAsync(200); await slideChangeComplete(slides[0], slides[1]); expect(carousel.current).to.equal(1); }); it('should properly call `igcSlideChanged` event', async () => { - const eventSpy = spy(carousel, 'emitEvent'); + const spy = vi.spyOn(carousel, 'emitEvent'); carousel.disableLoop = true; carousel.interval = 100; @@ -684,15 +690,15 @@ describe('Carousel', () => { expect(carousel.current).to.equal(0); - await clock.tickAsync(300); + await vi.advanceTimersByTimeAsync(300); expect(carousel.current).to.equal(2); - expect(eventSpy.callCount).to.equal(2); + expect(getEvents(spy, 'igcSlideChanged').length).to.equal(2); }); it('should pause/play on pointerenter/pointerleave', async () => { - const eventSpy = spy(carousel, 'emitEvent'); - const divContainer = carousel.shadowRoot?.querySelector( + const spy = vi.spyOn(carousel, 'emitEvent'); + const divContainer = carousel.renderRoot.querySelector( 'div[aria-live]' ) as HTMLDivElement; @@ -719,14 +725,14 @@ describe('Carousel', () => { expect(carousel.isPaused).to.be.false; expect(divContainer.ariaLive).to.equal('off'); - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcPaused'); - expect(eventSpy.secondCall).calledWith('igcPlaying'); + expect(spy.mock.calls.length).to.equal(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcPaused'); + expect(spy).toHaveBeenNthCalledWith(2, 'igcPlaying'); }); it('should pause/play on keyboard interaction', async () => { - const eventSpy = spy(carousel, 'emitEvent'); - const divContainer = carousel.shadowRoot?.querySelector( + const spy = vi.spyOn(carousel, 'emitEvent'); + const divContainer = carousel.renderRoot.querySelector( 'div[aria-live]' ) as HTMLDivElement; @@ -778,16 +784,16 @@ describe('Carousel', () => { expect(carousel.isPaused).to.be.false; expect(divContainer.ariaLive).to.equal('off'); - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcPaused'); - expect(eventSpy.secondCall).calledWith('igcPlaying'); + expect(spy.mock.calls.length).to.equal(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcPaused'); + expect(spy).toHaveBeenNthCalledWith(2, 'igcPlaying'); }); it('should pause when focusing an interactive element - issue #1731', async () => { carousel.interval = 200; await elementUpdated(carousel); - await clock.tickAsync(199); + await vi.advanceTimersByTimeAsync(199); expect(carousel.isPlaying).to.be.true; expect(carousel.isPaused).to.be.false; @@ -797,7 +803,7 @@ describe('Carousel', () => { carousel.dispatchEvent(new PointerEvent('pointerenter')); await elementUpdated(carousel); - await clock.tickAsync(1); + await vi.advanceTimersByTimeAsync(1); expect(carousel.isPlaying).to.be.false; expect(carousel.isPaused).to.be.true; @@ -811,7 +817,7 @@ describe('Carousel', () => { carousel.dispatchEvent(new PointerEvent('pointerleave')); await elementUpdated(carousel); - await clock.tickAsync(200); + await vi.advanceTimersByTimeAsync(200); // an interactive element is focused // -> should not start rotation on pointerleave @@ -823,7 +829,11 @@ describe('Carousel', () => { carousel.dispatchEvent(new FocusEvent('focusout')); await elementUpdated(carousel); - await clock.tickAsync(200); + await vi.advanceTimersByTimeAsync(200); + await slideChangeComplete(slides[0], slides[1]); + + await vi.advanceTimersByTimeAsync(200); + await slideChangeComplete(slides[1], slides[2]); // the interactive element loses focus // -> should start rotation @@ -833,7 +843,7 @@ describe('Carousel', () => { }); it('should not pause on interaction if `disablePauseOnInteraction` is true', async () => { - const eventSpy = spy(carousel, 'emitEvent'); + const spy = vi.spyOn(carousel, 'emitEvent'); const divContainer = carousel.shadowRoot?.querySelector( 'div[aria-live]' ) as HTMLDivElement; @@ -862,7 +872,7 @@ describe('Carousel', () => { expect(carousel.isPaused).to.be.false; expect(divContainer.ariaLive).to.equal('off'); - expect(eventSpy.callCount).to.equal(0); + expect(spy.mock.calls.length).to.equal(0); }); }); @@ -1073,13 +1083,13 @@ describe('Carousel', () => { 'div[aria-live="polite"]' ) as Element; - const eventSpy = spy(carousel, 'emitEvent'); - - const prevStub = stub(carousel, 'prev'); - const nextStub = stub(carousel, 'next'); + const spy = vi.spyOn(carousel, 'emitEvent'); - prevStub.resolves(false); - nextStub.onFirstCall().resolves(true).onSecondCall().resolves(false); + const prevStub = vi.spyOn(carousel, 'prev').mockResolvedValue(false); + const nextStub = vi + .spyOn(carousel, 'next') + .mockResolvedValueOnce(true) + .mockResolvedValueOnce(false); carousel.disableLoop = true; await elementUpdated(carousel); @@ -1104,19 +1114,19 @@ describe('Carousel', () => { simulateLostPointerCapture(carouselSlidesContainer); await slideChangeComplete(slides[0], slides[1]); - expect(eventSpy.callCount).to.equal(1); + expect(getEvents(spy, 'igcSlideChanged').length).to.equal(1); - eventSpy.resetHistory(); - prevStub.resetHistory(); - nextStub.resetHistory(); + spy.mockClear(); + prevStub.mockClear(); + nextStub.mockClear(); - prevStub.resolves(false); - nextStub.onFirstCall().resolves(true).onSecondCall().resolves(false); + prevStub.mockResolvedValue(false); + nextStub.mockResolvedValueOnce(true).mockResolvedValueOnce(false); carousel.vertical = true; await elementUpdated(carousel); - expect(eventSpy.callCount).to.equal(0); + expect(spy.mock.calls.length).to.equal(0); // swipe down - disabled simulatePointerDown(carouselSlidesContainer); @@ -1136,7 +1146,7 @@ describe('Carousel', () => { simulateLostPointerCapture(carouselSlidesContainer); await slideChangeComplete(slides[1], slides[0]); - expect(eventSpy.callCount).to.equal(1); + expect(getEvents(spy, 'igcSlideChanged').length).to.equal(1); }); }); }); diff --git a/src/components/chat/chat-input.ts b/src/components/chat/chat-input.ts index baecb42d1..c957557f8 100644 --- a/src/components/chat/chat-input.ts +++ b/src/components/chat/chat-input.ts @@ -98,7 +98,7 @@ export default class IgcChatInputComponent extends LitElement { private _userIsTyping = false; private _userLastTypeTime = Date.now(); - private _typingTimeout = 0; + private _typingTimeout?: ReturnType; private readonly _adoptedStyles = addAdoptedStylesController(this); diff --git a/src/components/chat/chat.spec.ts b/src/components/chat/chat.spec.ts index e4d073ab5..6bee093da 100644 --- a/src/components/chat/chat.spec.ts +++ b/src/components/chat/chat.spec.ts @@ -1,13 +1,23 @@ -import { elementUpdated, expect, fixture } from '@open-wc/testing'; import { html, nothing } from 'lit'; -import { spy, stub, useFakeTimers } from 'sinon'; +import { + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; import { configureTheme } from '../../theming/config.js'; import type IgcIconButtonComponent from '../button/icon-button.js'; import IgcChipComponent from '../chip/chip.js'; import { enterKey, tabKey } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture } from '../common/helpers.spec.js'; import { first, last } from '../common/util.js'; import { + expectCalledWith, + getEvents, isFocused, simulateBlur, simulateClick, @@ -31,7 +41,7 @@ import type { } from './types.js'; describe('Chat', () => { - before(() => { + beforeAll(() => { defineComponents(IgcChatComponent, IgcInputComponent); }); @@ -617,7 +627,7 @@ describe('Chat', () => { describe('Interactions', () => { describe('Click', () => { it('should update messages properly on send button click', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); const { textarea, sendButton } = getChatDOM(chat).input!; textarea.setAttribute('value', 'Hello!'); textarea.dispatchEvent( @@ -628,8 +638,11 @@ describe('Chat', () => { simulateClick(sendButton); await elementUpdated(chat); - expect(eventSpy).calledWith('igcMessageCreated'); - const eventArgs = eventSpy.getCall(1).args[1]?.detail as IgcChatMessage; + expect(spy).toHaveBeenCalledWith( + 'igcMessageCreated', + expect.anything() + ); + const eventArgs = spy.mock.calls[1]?.[1]?.detail as IgcChatMessage; const args = { ...eventArgs, text: 'Hello!', sender: 'user' }; expect(eventArgs).to.deep.equal(args); expect(chat.messages.length).to.equal(1); @@ -640,7 +653,7 @@ describe('Chat', () => { }); it('should update messages properly on suggestion chip click', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); chat.options = { suggestions: ['Suggestion 1', 'Suggestion 2'], }; @@ -654,8 +667,11 @@ describe('Chat', () => { simulateClick(suggestionItems[0]); await elementUpdated(chat); - expect(eventSpy).calledWith('igcMessageCreated'); - const eventArgs = eventSpy.getCall(0).args[1]?.detail; + expect(spy).toHaveBeenCalledWith( + 'igcMessageCreated', + expect.anything() + ); + const eventArgs = spy.mock.calls[0]?.[1]?.detail; const args = eventArgs && typeof eventArgs === 'object' ? { ...eventArgs, text: 'Suggestion 1', sender: 'user' } @@ -670,13 +686,16 @@ describe('Chat', () => { }); it('should remove attachment on chip remove button click', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); const fileInput = getChatDOM(chat).input.fileInput; simulateFileUpload(fileInput, files); await elementUpdated(chat); - expect(eventSpy).calledOnce; - expect(eventSpy.calledWith('igcAttachmentAdded')).to.be.true; + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'igcAttachmentAdded', + expect.anything() + ); const attachments = getChatDOM(chat).input.chips; expect(attachments.length).to.equal(2); @@ -686,9 +705,12 @@ describe('Chat', () => { simulateClick(removeFileButton); await elementUpdated(chat); - expect(eventSpy).calledTwice; - expect(eventSpy.calledWith('igcAttachmentRemoved')).to.be.true; - const detail = eventSpy.getCall(1).args[1]?.detail; + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenCalledWith( + 'igcAttachmentRemoved', + expect.anything() + ); + const detail = spy.mock.calls[1]?.[1]?.detail; expect((detail as IgcChatMessageAttachment).name).to.equal( files[1].name ); @@ -761,14 +783,13 @@ describe('Chat', () => { }); it('should handle the copy action properly', async () => { - const clipboardWriteText = stub( - navigator.clipboard, - 'writeText' - ).resolves(); + const clipboardWriteText = vi + .spyOn(navigator.clipboard, 'writeText') + .mockResolvedValue(); chat.messages = [messages[0]]; await elementUpdated(chat); - expect(clipboardWriteText.called).to.be.false; + expect(clipboardWriteText).not.toHaveBeenCalled(); const firstMessage = chat.shadowRoot?.querySelectorAll('igc-chat-message')[0]; // click on copy icon @@ -777,7 +798,7 @@ describe('Chat', () => { ) as HTMLElement; simulateClick(copyIcon); await elementUpdated(chat); - expect(clipboardWriteText.called).to.be.true; + expect(clipboardWriteText).toHaveBeenCalled(); }); }); @@ -792,7 +813,7 @@ describe('Chat', () => { }); it('should be able to drag & drop files based on the types listed in `acceptedFiles`', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); const inputArea = getChatDOM(chat).input.self!; const dropZone = inputArea?.renderRoot.querySelector( `div[part='input-container']` @@ -817,8 +838,7 @@ describe('Chat', () => { dropZone?.dispatchEvent(dragEnterEvent); await elementUpdated(chat); - expect(eventSpy).calledOnce; - expect(eventSpy).calledWith('igcAttachmentDrag'); + expectCalledWith(spy, 'igcAttachmentDrag'); const dropEvent = new DragEvent('drop', { bubbles: true, @@ -831,19 +851,21 @@ describe('Chat', () => { dropZone.dispatchEvent(dropEvent); await elementUpdated(chat); - expect(eventSpy).calledWith('igcAttachmentDrop'); + expectCalledWith(spy, 'igcAttachmentDrop'); const attachments = getChatDOM(chat).input.chips; expect(attachments?.length).to.equal(1); expect(attachments?.[0]?.textContent?.trim()).to.equal('test.txt'); - expect(eventSpy).calledWith('igcAttachmentDrop'); - expect(eventSpy).calledWith('igcAttachmentAdded'); + expect(spy).toHaveBeenCalledWith( + 'igcAttachmentAdded', + expect.anything() + ); } }); }); describe('Keyboard', () => { it('should update messages properly on `Enter` keypress when the textarea is focused', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); const textArea = getChatDOM(chat).input.textarea; textArea.setAttribute('value', 'Hello!'); @@ -855,8 +877,11 @@ describe('Chat', () => { simulateKeyboard(textArea, enterKey); await elementUpdated(chat); - expect(eventSpy).calledWith('igcMessageCreated'); - const eventArgs = eventSpy.getCall(2).args[1]?.detail; + expect(spy).toHaveBeenCalledWith( + 'igcMessageCreated', + expect.anything() + ); + const eventArgs = spy.mock.calls[2]?.[1]?.detail; const args = eventArgs && typeof eventArgs === 'object' ? { ...eventArgs, text: 'Hello!', sender: 'user' } @@ -874,7 +899,7 @@ describe('Chat', () => { describe('Events', () => { it('emits igcAttachmentClick', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); chat.messages = [messages[1]]; await elementUpdated(chat); @@ -883,35 +908,37 @@ describe('Chat', () => { const attachmentHeader = getChatAttachmentDOM(attachment).header; simulateClick(attachmentHeader); - expect(eventSpy).calledWith('igcAttachmentClick', { + expect(spy).toHaveBeenCalledWith('igcAttachmentClick', { detail: { ...messages[1].attachments?.at(0) }, }); }); it('emits igcTypingChange', async () => { - const clock = useFakeTimers({ now: 0, toFake: ['Date', 'setTimeout'] }); + vi.useFakeTimers({ now: 0, toFake: ['Date', 'setTimeout'] }); - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); const textArea = getChatDOM(chat).input.textarea; chat.options = { stopTypingDelay: 2500 }; simulateKeyboard(textArea, 'a', 15); await elementUpdated(chat); - expect(eventSpy).calledWith('igcTypingChange'); - expect(eventSpy.firstCall.args[1]?.detail).to.be.true; + expect(spy).toHaveBeenCalledWith('igcTypingChange', expect.anything()); + expect(last(first(getEvents(spy, 'igcTypingChange'))).detail).to.be + .true; - clock.setSystemTime(2501); - await clock.runAllAsync(); + vi.setSystemTime(2501); + await vi.runAllTimersAsync(); - expect(eventSpy).calledWith('igcTypingChange'); - expect(eventSpy.lastCall.args[1]?.detail).to.be.false; + expect(spy).toHaveBeenCalledWith('igcTypingChange', expect.anything()); + expect(last(last(getEvents(spy, 'igcTypingChange'))).detail).to.be + .false; - clock.restore(); + vi.useRealTimers(); }); it('emits igcTypingChange after sending a message', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); const textArea = getChatDOM(chat).input.textarea; const internalInput = textArea.renderRoot.querySelector('textarea')!; @@ -941,12 +968,12 @@ describe('Chat', () => { ]; for (const [idx, event] of expectedEventSequence.entries()) { - expect(eventSpy.getCall(idx).firstArg).to.equal(event); + expect(spy.mock.calls[idx][0]).to.equal(event); } }); it('should not emit igcTypingChange on Tab key', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); const textArea = getChatDOM(chat).input.textarea; const internalInput = textArea.renderRoot.querySelector('textarea')!; @@ -955,37 +982,37 @@ describe('Chat', () => { simulateKeyboard(internalInput, tabKey); await elementUpdated(chat); - expect(eventSpy.getCalls()).is.empty; + expect(spy.mock.calls).to.be.empty; }); it('emits igcInputFocus', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); simulateFocus(getChatDOM(chat).input.textarea); - expect(eventSpy).calledWith('igcInputFocus'); + expectCalledWith(spy, 'igcInputFocus'); }); it('emits igcInputBlur', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); simulateBlur(getChatDOM(chat).input.textarea); - expect(eventSpy).calledWith('igcInputBlur'); + expectCalledWith(spy, 'igcInputBlur'); }); it('emits igcInputChange', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); const textArea = getChatDOM(chat).input.textarea!; textArea.setAttribute('value', 'Hello!'); textArea.dispatchEvent(new CustomEvent('igcInput', { detail: 'Hello!' })); await elementUpdated(chat); - expect(eventSpy).calledWith('igcInputChange', { + expect(spy).toHaveBeenCalledWith('igcInputChange', { detail: { value: 'Hello!' }, }); }); it('emits igcMessageReact', async () => { - const eventSpy = spy(chat, 'emitEvent'); + const spy = vi.spyOn(chat, 'emitEvent'); chat.messages = [messages[0]]; await elementUpdated(chat); @@ -994,7 +1021,7 @@ describe('Chat', () => { getChatMessageDOM(messageElement).defaultActionButtons[1]; simulateClick(likeIcon); - expect(eventSpy).calledWith('igcMessageReact', { + expect(spy).toHaveBeenCalledWith('igcMessageReact', { detail: { message: messages[0], reaction: 'thumb_up_active' }, }); }); diff --git a/src/components/checkbox/checkbox.spec.ts b/src/components/checkbox/checkbox.spec.ts index bf13e12db..382948a58 100644 --- a/src/components/checkbox/checkbox.spec.ts +++ b/src/components/checkbox/checkbox.spec.ts @@ -1,7 +1,7 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import { spy } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { configureTheme } from '../../theming/config.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, isFocused, @@ -13,7 +13,7 @@ import { import IgcCheckboxComponent from './checkbox.js'; describe('Checkbox', () => { - before(() => { + beforeAll(() => { defineComponents(IgcCheckboxComponent); }); @@ -171,21 +171,21 @@ describe('Checkbox', () => { }); it('should emit click event only once', async () => { - const eventSpy = spy(element, 'click'); + const spy = vi.spyOn(element, 'click'); - element.addEventListener('click', eventSpy); + element.addEventListener('click', spy); element.click(); await elementUpdated(element); - expect(eventSpy.callCount).to.equal(1); + expect(spy.mock.calls.length).to.equal(1); }); it('should emit igcChange event when the checkbox checked state changes', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); element.click(); await elementUpdated(element); - expect(eventSpy).calledWithExactly('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: { checked: true, value: undefined }, }); }); diff --git a/src/components/checkbox/switch.spec.ts b/src/components/checkbox/switch.spec.ts index 815d647bc..9129f8375 100644 --- a/src/components/checkbox/switch.spec.ts +++ b/src/components/checkbox/switch.spec.ts @@ -1,6 +1,6 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import { spy } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, isFocused, @@ -8,7 +8,7 @@ import { import IgcSwitchComponent from './switch.js'; describe('Switch', () => { - before(() => { + beforeAll(() => { defineComponents(IgcSwitchComponent); }); @@ -133,11 +133,11 @@ describe('Switch', () => { }); it('should emit igcChange event when the switch checked state changes', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); element.click(); await elementUpdated(element); - expect(eventSpy).calledWithExactly('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: { checked: true, value: undefined }, }); }); diff --git a/src/components/chip/chip.spec.ts b/src/components/chip/chip.spec.ts index b4dbf7684..7abfc27b1 100644 --- a/src/components/chip/chip.spec.ts +++ b/src/components/chip/chip.spec.ts @@ -1,5 +1,6 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; +import { beforeAll, describe, expect, it } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import IgcChipComponent from './chip.js'; describe('Chip', () => { @@ -7,7 +8,7 @@ describe('Chip', () => { ignoreAttributes: ['style'], }; - before(() => { + beforeAll(() => { defineComponents(IgcChipComponent); }); diff --git a/src/components/combo/combo.spec.ts b/src/components/combo/combo.spec.ts index fb0d1a1b0..1e2caf921 100644 --- a/src/components/combo/combo.spec.ts +++ b/src/components/combo/combo.spec.ts @@ -1,6 +1,4 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import { spy } from 'sinon'; - +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { altKey, arrowDown, @@ -13,9 +11,12 @@ import { tabKey, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { first } from '../common/util.js'; import { createFormAssociatedTestBed, + expectCalledWith, + expectNotCalledWith, isFocused, simulateClick, simulateKeyboard, @@ -174,7 +175,7 @@ describe('Combo', () => { .shadowRoot!.querySelector('igc-combo-list')! .querySelectorAll('[part~="group-header"]'), ] as IgcComboHeaderComponent[]; - before(() => { + beforeAll(() => { defineComponents(IgcComboComponent); }); @@ -279,25 +280,25 @@ describe('Combo', () => { }); it('should open the menu upon clicking on the input', async () => { - const eventSpy = spy(combo, 'emitEvent'); + const spy = vi.spyOn(combo, 'emitEvent'); simulateClick(input); await elementUpdated(combo); - expect(eventSpy).calledWith('igcOpening'); - expect(eventSpy).calledWith('igcOpened'); + expectCalledWith(spy, 'igcOpening'); + expectCalledWith(spy, 'igcOpened'); expect(combo.open).to.be.true; }); it('should hide the menu upon clicking on the input', async () => { - const eventSpy = spy(combo, 'emitEvent'); + const spy = vi.spyOn(combo, 'emitEvent'); await combo.show(); simulateClick(input); await elementUpdated(combo); - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).calledWith('igcClosed'); + expectCalledWith(spy, 'igcClosing'); + expectCalledWith(spy, 'igcClosed'); expect(combo.open).to.be.false; }); @@ -306,15 +307,15 @@ describe('Combo', () => { combo.addEventListener('igcOpening', (event: CustomEvent) => { event.preventDefault(); }); - const eventSpy = spy(combo, 'emitEvent'); + const spy = vi.spyOn(combo, 'emitEvent'); simulateClick(input); await elementUpdated(combo); - expect(eventSpy).calledOnceWithExactly('igcOpening', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcOpening', { cancelable: true, }); - expect(eventSpy).not.calledWith('igcOpened'); + expectNotCalledWith(spy, 'igcOpened'); }); it('should be able to cancel the igcClosing event', async () => { @@ -322,15 +323,15 @@ describe('Combo', () => { combo.addEventListener('igcClosing', (event: CustomEvent) => { event.preventDefault(); }); - const eventSpy = spy(combo, 'emitEvent'); + const spy = vi.spyOn(combo, 'emitEvent'); simulateClick(input); await elementUpdated(combo); - expect(eventSpy).calledOnceWithExactly('igcClosing', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcClosing', { cancelable: true, }); - expect(eventSpy).not.calledWith('igcClosed'); + expectNotCalledWith(spy, 'igcClosed'); }); it('should focus the input when the host is focused', async () => { @@ -507,12 +508,12 @@ describe('Combo', () => { event.preventDefault() ); - const eventSpy = spy(combo, 'emitEvent'); - expect(eventSpy).not.calledWith('igcChange'); + const spy = vi.spyOn(combo, 'emitEvent'); + expectNotCalledWith(spy, 'igcChange'); }); it('should fire igcChange selection type event on mouse click', async () => { - const eventSpy = spy(combo, 'emitEvent'); + const spy = vi.spyOn(combo, 'emitEvent'); const args = { cancelable: true, detail: { @@ -528,11 +529,11 @@ describe('Combo', () => { first(items(combo)).click(); expect(combo.value).to.eql(['BG01']); - expect(eventSpy).calledWithExactly('igcChange', args); + expect(spy).toHaveBeenCalledWith('igcChange', args); }); it('should fire igcChange deselection type event on mouse click', async () => { - const eventSpy = spy(combo, 'emitEvent'); + const spy = vi.spyOn(combo, 'emitEvent'); const args = { cancelable: true, detail: { @@ -553,14 +554,14 @@ describe('Combo', () => { await elementUpdated(combo); expect(combo.value).to.eql(['BG02', 'BG03']); - expect(eventSpy).calledWithExactly('igcChange', args); + expect(spy).toHaveBeenCalledWith('igcChange', args); }); it('should be able to cancel the selection event', async () => { combo.addEventListener('igcChange', (event: CustomEvent) => { event.preventDefault(); }); - const eventSpy = spy(combo, 'emitEvent'); + const spy = vi.spyOn(combo, 'emitEvent'); combo.open = true; await elementUpdated(combo); @@ -569,7 +570,7 @@ describe('Combo', () => { first(items(combo)).click(); await elementUpdated(combo); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); expect(combo.value).to.be.empty; }); @@ -577,7 +578,7 @@ describe('Combo', () => { combo.addEventListener('igcChange', (event: CustomEvent) => { event.preventDefault(); }); - const eventSpy = spy(combo, 'emitEvent'); + const spy = vi.spyOn(combo, 'emitEvent'); combo.select(['BG01', 'BG02']); combo.open = true; @@ -587,7 +588,7 @@ describe('Combo', () => { first(items(combo)).click(); await elementUpdated(combo); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); expect(combo.value).lengthOf(2); }); diff --git a/src/components/common/controllers/drag.spec.ts b/src/components/common/controllers/drag.spec.ts index 162afb862..85c045c70 100644 --- a/src/components/common/controllers/drag.spec.ts +++ b/src/components/common/controllers/drag.spec.ts @@ -1,13 +1,21 @@ +import { css, LitElement } from 'lit'; +import { + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + type MockInstance, + vi, +} from 'vitest'; import { defineCE, elementUpdated, - expect, fixture, html, unsafeStatic, -} from '@open-wc/testing'; -import { css, LitElement } from 'lit'; -import { type SinonSpy, spy } from 'sinon'; +} from '../helpers.spec.js'; import { getCenterPoint, last } from '../util.js'; import { compareStyles, @@ -27,7 +35,7 @@ describe('Drag controller', () => { let tag: string; let instance: DragElement; - before(() => { + beforeAll(() => { tag = defineCE( class extends LitElement { public static override styles = css` @@ -48,7 +56,7 @@ describe('Drag controller', () => { }); describe('Immediate mode - basic element dragging', () => { - const dragStart = spy(); + const dragStart = vi.fn(); beforeEach(async () => { const tagName = unsafeStatic(tag); @@ -66,7 +74,7 @@ describe('Drag controller', () => { }); afterEach(() => { - dragStart.resetHistory(); + dragStart.mockClear(); }); it('should not start drag operation when disabled', async () => { @@ -75,7 +83,7 @@ describe('Drag controller', () => { simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.false; + expect(dragStart).not.toHaveBeenCalled(); }); it('should not start a drag operation on a non-primary button interaction', async () => { @@ -84,18 +92,18 @@ describe('Drag controller', () => { simulatePointerDown(instance, { button: 1 }); await elementUpdated(instance); - expect(dragStart.called).is.false; + expect(dragStart).not.toHaveBeenCalled(); }); it('should not start a drag operation when a skip callback returns true', async () => { - const skip = spy(() => true); + const skip = vi.fn(() => true); instance.controller.set({ skip, start: dragStart }); simulatePointerDown(instance); await elementUpdated(instance); - expect(skip.called).is.true; - expect(dragStart.called).is.false; + expect(skip).toHaveBeenCalled(); + expect(dragStart).not.toHaveBeenCalled(); }); it('should apply correct internal styles on drag operation', async () => { @@ -109,25 +117,25 @@ describe('Drag controller', () => { }); it('should not create a ghost element in "immediate" mode', async () => { - const ghost = spy(); + const ghost = vi.fn(); instance.controller.set({ start: dragStart, ghost }); simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.true; - expect(ghost.called).is.false; + expect(dragStart).toHaveBeenCalled(); + expect(ghost).not.toHaveBeenCalled(); }); it('should not invoke the `layer` configuration callback in "immediate" mode', async () => { - const layer = spy(); + const layer = vi.fn(); instance.controller.set({ start: dragStart, layer }); simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.true; - expect(layer.called).is.false; + expect(dragStart).toHaveBeenCalled(); + expect(layer).not.toHaveBeenCalled(); }); it('should invoke start callback on drag operation', async () => { @@ -136,23 +144,23 @@ describe('Drag controller', () => { simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.true; - expect(dragStart.callCount).to.equal(1); + expect(dragStart).toHaveBeenCalled(); + expect(dragStart).toHaveBeenCalledTimes(1); }); it('should not invoke move unless a start is invoked', async () => { - const dragMove = spy(); + const dragMove = vi.fn(); instance.controller.set({ start: dragStart, move: dragMove }); simulatePointerMove(instance); await elementUpdated(instance); - expect(dragStart.called).is.false; - expect(dragMove.called).is.false; + expect(dragStart).not.toHaveBeenCalled(); + expect(dragMove).not.toHaveBeenCalled(); }); it('should invoke move when moving the dragged element around the viewport', async () => { - const dragMove = spy(); + const dragMove = vi.fn(); instance.controller.set({ start: dragStart, move: dragMove }); simulatePointerDown(instance); @@ -164,55 +172,55 @@ describe('Drag controller', () => { ); await elementUpdated(instance); - expect(dragStart.called).is.true; - expect(dragMove.called).is.true; - expect(dragMove.callCount).to.equal(10); + expect(dragStart).toHaveBeenCalled(); + expect(dragMove).toHaveBeenCalled(); + expect(dragMove).toHaveBeenCalledTimes(10); }); it('should invoke end when releasing the dragged element', async () => { - const dragEnd = spy(); + const dragEnd = vi.fn(); instance.controller.set({ start: dragStart, end: dragEnd }); simulatePointerDown(instance); simulateLostPointerCapture(instance); await elementUpdated(instance); - expect(dragStart.callCount).to.equal(1); - expect(dragEnd.callCount).to.equal(1); + expect(dragStart).toHaveBeenCalledTimes(1); + expect(dragEnd).toHaveBeenCalledTimes(1); }); it('should invoke cancel when pressing Escape during a drag operation', async () => { - const dragCancel = spy(); + const dragCancel = vi.fn(); instance.controller.set({ start: dragStart, cancel: dragCancel }); simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.true; + expect(dragStart).toHaveBeenCalled(); simulateKeyboard(instance, escapeKey); await elementUpdated(instance); - expect(dragCancel.called).is.true; + expect(dragCancel).toHaveBeenCalled(); }); it('should not invoke cancel when pressing Escape outside of drag operation', async () => { // Sanity check since the Escape key handler is a root level dynamic listener. - const dragCancel = spy(); + const dragCancel = vi.fn(); instance.controller.set({ cancel: dragCancel }); simulateKeyboard(instance, escapeKey); await elementUpdated(instance); - expect(dragCancel.called).is.false; + expect(dragCancel).not.toHaveBeenCalled(); }); }); describe('Immediate mode - advanced element dragging', () => { - const dragStart = spy(); + const dragStart = vi.fn(); - function getCallbackArgs(fn: SinonSpy) { - return last(last(fn.args)) as DragCallbackParameters; + function getCallbackArgs(fn: MockInstance) { + return last(last(fn.mock.calls)) as DragCallbackParameters; } beforeEach(async () => { @@ -234,45 +242,45 @@ describe('Drag controller', () => { }); afterEach(() => { - dragStart.resetHistory(); + dragStart.mockClear(); }); it('should not start a drag operation when `skip` is set and returns true', async () => { const button = instance.querySelector('button')!; - const skip = spy((event: PointerEvent) => event.target === button); + const skip = vi.fn((event: PointerEvent) => event.target === button); instance.controller.set({ start: dragStart, skip }); simulatePointerDown(button); await elementUpdated(instance); - expect(dragStart.called).is.false; - expect(skip.called).is.true; - expect(skip.returned(true)).is.true; + expect(dragStart).not.toHaveBeenCalled(); + expect(skip).toHaveBeenCalled(); + expect(skip).toHaveReturnedWith(true); simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.true; - expect(skip.called).is.true; - expect(skip.returned(false)).is.true; + expect(dragStart).toHaveBeenCalled(); + expect(skip).toHaveBeenCalled(); + expect(skip).toHaveReturnedWith(false); }); it('should start a drag operation only from the element returned from the `trigger` invocation', async () => { const button = instance.querySelector('button')!; - const trigger = spy(() => button); + const trigger = vi.fn(() => button); instance.controller.set({ start: dragStart, trigger }); simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.false; + expect(dragStart).not.toHaveBeenCalled(); simulatePointerDown(button); await elementUpdated(instance); - expect(dragStart.called).is.true; + expect(dragStart).toHaveBeenCalled(); }); it('should adhere to `snapToCursor` option on drag start', async () => { @@ -321,7 +329,7 @@ describe('Drag controller', () => { }); it('should pass correct parameter state in the move callback', async () => { - const move = spy(); + const move = vi.fn(); instance.controller.set({ start: dragStart, move }); const { x: clientX, y: clientY } = getCenterPoint(instance); @@ -341,7 +349,7 @@ describe('Drag controller', () => { }); it('should pass correct parameter state in end callback', async () => { - const end = spy(); + const end = vi.fn(); instance.controller.set({ end }); const { x: clientX, y: clientY } = getCenterPoint(instance); @@ -359,8 +367,8 @@ describe('Drag controller', () => { it('should invoke the `enter` callback when the `match` callback returns true', async () => { const target = document.querySelector('.target')!; - const matchTarget = spy((element: Element) => target === element); - const enter = spy(); + const matchTarget = vi.fn((element: Element) => target === element); + const enter = vi.fn(); const { x: clientX, y: clientY } = target.getBoundingClientRect(); @@ -370,16 +378,16 @@ describe('Drag controller', () => { simulatePointerMove(instance, { clientX, clientY }); await elementUpdated(instance); - expect(matchTarget.called).is.true; - expect(enter.called).is.true; + expect(matchTarget).toHaveBeenCalled(); + expect(enter).toHaveBeenCalled(); expect(getCallbackArgs(enter).state.element).to.eql(target); }); it('should not invoke the `enter` callback when the `match` callback returns false', async () => { const target = document.querySelector('.target')!; - const matchTarget = spy(() => false); - const enter = spy(); + const matchTarget = vi.fn(() => false); + const enter = vi.fn(); const { x: clientX, y: clientY } = target.getBoundingClientRect(); @@ -389,15 +397,15 @@ describe('Drag controller', () => { simulatePointerMove(instance, { clientX, clientY }); await elementUpdated(instance); - expect(matchTarget.called).is.true; - expect(enter.called).is.false; + expect(matchTarget).toHaveBeenCalled(); + expect(enter).not.toHaveBeenCalled(); }); it('should invoke the `leave` callback when leaving the boundaries of a matched element', async () => { const target = document.querySelector('.target')!; - const matchTarget = spy((element: Element) => target === element); - const enter = spy(); - const leave = spy(); + const matchTarget = vi.fn((element: Element) => target === element); + const enter = vi.fn(); + const leave = vi.fn(); const { x: clientX, y: clientY } = target.getBoundingClientRect(); @@ -407,22 +415,22 @@ describe('Drag controller', () => { simulatePointerMove(instance, { clientX, clientY }); await elementUpdated(instance); - expect(matchTarget.called).is.true; - expect(enter.called).is.true; + expect(matchTarget).toHaveBeenCalled(); + expect(enter).toHaveBeenCalled(); expect(getCallbackArgs(enter).state.element).to.eql(target); simulatePointerMove(instance, { clientX: 0, clientY: 0 }); await elementUpdated(instance); - expect(leave.called).is.true; + expect(leave).toHaveBeenCalled(); expect(getCallbackArgs(leave).state.element).to.eql(target); }); it('should invoke the `over` callback while dragging over a matched element', async () => { const target = document.querySelector('.target')!; - const matchTarget = spy((element: Element) => target === element); - const over = spy(); + const matchTarget = vi.fn((element: Element) => target === element); + const over = vi.fn(); instance.controller.set({ matchTarget, over }); @@ -433,19 +441,19 @@ describe('Drag controller', () => { await elementUpdated(instance); // Not called on initial drag enter - expect(over.called).is.false; + expect(over).not.toHaveBeenCalled(); // 5 pointer moves over the matched element simulatePointerMove(instance, { clientX, clientY }, { x: 5, y: 5 }, 5); await elementUpdated(instance); - expect(over.callCount).to.equal(5); + expect(over).toHaveBeenCalledTimes(5); expect(getCallbackArgs(over).state.element).to.eql(target); }); }); describe('Deferred mode - basic element dragging', () => { - const dragStart = spy(); + const dragStart = vi.fn(); function createGhost() { const clone = instance.cloneNode() as HTMLElement; @@ -479,7 +487,7 @@ describe('Drag controller', () => { }); afterEach(() => { - dragStart.resetHistory(); + dragStart.mockClear(); }); it('should not start drag operation when disabled', async () => { @@ -488,7 +496,7 @@ describe('Drag controller', () => { simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.false; + expect(dragStart).not.toHaveBeenCalled(); }); it('should not start a drag operation on a non-primary button interaction', async () => { @@ -497,18 +505,18 @@ describe('Drag controller', () => { simulatePointerDown(instance, { button: 1 }); await elementUpdated(instance); - expect(dragStart.called).is.false; + expect(dragStart).not.toHaveBeenCalled(); }); it('should not start a drag operation when a skip callback returns true', async () => { - const skip = spy(() => true); + const skip = vi.fn(() => true); instance.controller.set({ skip, start: dragStart }); simulatePointerDown(instance); await elementUpdated(instance); - expect(skip.called).is.true; - expect(dragStart.called).is.false; + expect(skip).toHaveBeenCalled(); + expect(dragStart).not.toHaveBeenCalled(); }); it('should apply correct internal styles on drag operation', async () => { @@ -553,7 +561,7 @@ describe('Drag controller', () => { }); it('should correctly fallback to the host as a container if the layer callbacks return falsy', async () => { - const layer = spy((): HTMLElement => null as unknown as HTMLElement); + const layer = vi.fn((): HTMLElement => null as unknown as HTMLElement); instance.controller.set({ layer }); simulatePointerDown(instance); @@ -563,7 +571,7 @@ describe('Drag controller', () => { }); it('should correctly place the ghost element in the configured layer container', async () => { - const layer = spy(() => instance.parentElement!); + const layer = vi.fn(() => instance.parentElement!); instance.controller.set({ layer }); simulatePointerDown(instance); @@ -578,23 +586,23 @@ describe('Drag controller', () => { simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.true; - expect(dragStart.callCount).to.equal(1); + expect(dragStart).toHaveBeenCalled(); + expect(dragStart).toHaveBeenCalledTimes(1); }); it('should not invoke move unless a start is invoked', async () => { - const dragMove = spy(); + const dragMove = vi.fn(); instance.controller.set({ start: dragStart, move: dragMove }); simulatePointerMove(instance); await elementUpdated(instance); - expect(dragStart.called).is.false; - expect(dragMove.called).is.false; + expect(dragStart).not.toHaveBeenCalled(); + expect(dragMove).not.toHaveBeenCalled(); }); it('should invoke move when moving the dragged element around the viewport', async () => { - const dragMove = spy(); + const dragMove = vi.fn(); instance.controller.set({ start: dragStart, move: dragMove }); simulatePointerDown(instance); @@ -606,57 +614,57 @@ describe('Drag controller', () => { ); await elementUpdated(instance); - expect(dragStart.called).is.true; - expect(dragMove.called).is.true; - expect(dragMove.callCount).to.equal(10); + expect(dragStart).toHaveBeenCalled(); + expect(dragMove).toHaveBeenCalled(); + expect(dragMove).toHaveBeenCalledTimes(10); }); it('should invoke end when releasing the dragged element', async () => { - const dragEnd = spy(); + const dragEnd = vi.fn(); instance.controller.set({ start: dragStart, end: dragEnd }); simulatePointerDown(instance); simulateLostPointerCapture(instance); await elementUpdated(instance); - expect(dragStart.callCount).to.equal(1); - expect(dragEnd.callCount).to.equal(1); + expect(dragStart).toHaveBeenCalledTimes(1); + expect(dragEnd).toHaveBeenCalledTimes(1); expect(getDefaultGhost()).is.null; }); it('should invoke cancel when pressing Escape during a drag operation', async () => { - const dragCancel = spy(() => instance.controller.dispose()); + const dragCancel = vi.fn(() => instance.controller.dispose()); instance.controller.set({ start: dragStart, cancel: dragCancel }); simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.true; + expect(dragStart).toHaveBeenCalled(); simulateKeyboard(instance, escapeKey); await elementUpdated(instance); - expect(dragCancel.called).is.true; + expect(dragCancel).toHaveBeenCalled(); expect(getDefaultGhost()).is.null; }); it('should not invoke cancel when pressing Escape outside of drag operation', async () => { // Sanity check since the Escape key handler is a root level dynamic listener. - const dragCancel = spy(); + const dragCancel = vi.fn(); instance.controller.set({ cancel: dragCancel }); simulateKeyboard(instance, escapeKey); await elementUpdated(instance); - expect(dragCancel.called).is.false; + expect(dragCancel).not.toHaveBeenCalled(); }); }); describe('Deferred mode - advanced element dragging', () => { - const dragStart = spy(); + const dragStart = vi.fn(); - function getCallbackArgs(fn: SinonSpy) { - return last(last(fn.args)) as DragCallbackParameters; + function getCallbackArgs(fn: MockInstance) { + return last(last(fn.mock.calls)) as DragCallbackParameters; } beforeEach(async () => { @@ -678,45 +686,45 @@ describe('Drag controller', () => { }); afterEach(() => { - dragStart.resetHistory(); + dragStart.mockClear(); }); it('should not start a drag operation when `skip` is set and returns true', async () => { const button = instance.querySelector('button')!; - const skip = spy((event: PointerEvent) => event.target === button); + const skip = vi.fn((event: PointerEvent) => event.target === button); instance.controller.set({ start: dragStart, skip }); simulatePointerDown(button); await elementUpdated(instance); - expect(dragStart.called).is.false; - expect(skip.called).is.true; - expect(skip.returned(true)).is.true; + expect(dragStart).not.toHaveBeenCalled(); + expect(skip).toHaveBeenCalled(); + expect(skip).toHaveReturnedWith(true); simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.true; - expect(skip.called).is.true; - expect(skip.returned(false)).is.true; + expect(dragStart).toHaveBeenCalled(); + expect(skip).toHaveBeenCalled(); + expect(skip).toHaveReturnedWith(false); }); it('should start a drag operation only from the element returned from the `trigger` invocation', async () => { const button = instance.querySelector('button')!; - const trigger = spy(() => button); + const trigger = vi.fn(() => button); instance.controller.set({ start: dragStart, trigger }); simulatePointerDown(instance); await elementUpdated(instance); - expect(dragStart.called).is.false; + expect(dragStart).not.toHaveBeenCalled(); simulatePointerDown(button); await elementUpdated(instance); - expect(dragStart.called).is.true; + expect(dragStart).toHaveBeenCalled(); }); it('should adhere to `snapToCursor` option on drag start', async () => { @@ -765,7 +773,7 @@ describe('Drag controller', () => { }); it('should pass correct parameter state in the move callback', async () => { - const move = spy(); + const move = vi.fn(); instance.controller.set({ start: dragStart, move }); const { x: clientX, y: clientY } = getCenterPoint(instance); @@ -785,7 +793,7 @@ describe('Drag controller', () => { }); it('should pass correct parameter state in end callback', async () => { - const end = spy(); + const end = vi.fn(); instance.controller.set({ end }); const { x: clientX, y: clientY } = getCenterPoint(instance); @@ -803,8 +811,8 @@ describe('Drag controller', () => { it('should invoke the `enter` callback when the `match` callback returns true', async () => { const target = document.querySelector('.target')!; - const matchTarget = spy((element: Element) => target === element); - const enter = spy(); + const matchTarget = vi.fn((element: Element) => target === element); + const enter = vi.fn(); const { x: clientX, y: clientY } = target.getBoundingClientRect(); @@ -814,16 +822,16 @@ describe('Drag controller', () => { simulatePointerMove(instance, { clientX, clientY }); await elementUpdated(instance); - expect(matchTarget.called).is.true; - expect(enter.called).is.true; + expect(matchTarget).toHaveBeenCalled(); + expect(enter).toHaveBeenCalled(); expect(getCallbackArgs(enter).state.element).to.eql(target); }); it('should not invoke the `enter` callback when the `match` callback returns false', async () => { const target = document.querySelector('.target')!; - const matchTarget = spy(() => false); - const enter = spy(); + const matchTarget = vi.fn(() => false); + const enter = vi.fn(); const { x: clientX, y: clientY } = target.getBoundingClientRect(); @@ -833,15 +841,15 @@ describe('Drag controller', () => { simulatePointerMove(instance, { clientX, clientY }); await elementUpdated(instance); - expect(matchTarget.called).is.true; - expect(enter.called).is.false; + expect(matchTarget).toHaveBeenCalled(); + expect(enter).not.toHaveBeenCalled(); }); it('should invoke the `leave` callback when leaving the boundaries of a matched element', async () => { const target = document.querySelector('.target')!; - const matchTarget = spy((element: Element) => target === element); - const enter = spy(); - const leave = spy(); + const matchTarget = vi.fn((element: Element) => target === element); + const enter = vi.fn(); + const leave = vi.fn(); const { x: clientX, y: clientY } = target.getBoundingClientRect(); @@ -851,22 +859,22 @@ describe('Drag controller', () => { simulatePointerMove(instance, { clientX, clientY }); await elementUpdated(instance); - expect(matchTarget.called).is.true; - expect(enter.called).is.true; + expect(matchTarget).toHaveBeenCalled(); + expect(enter).toHaveBeenCalled(); expect(getCallbackArgs(enter).state.element).to.eql(target); simulatePointerMove(instance, { clientX: 0, clientY: 0 }); await elementUpdated(instance); - expect(leave.called).is.true; + expect(leave).toHaveBeenCalled(); expect(getCallbackArgs(leave).state.element).to.eql(target); }); it('should invoke the `over` callback while dragging over a matched element', async () => { const target = document.querySelector('.target')!; - const matchTarget = spy((element: Element) => target === element); - const over = spy(); + const matchTarget = vi.fn((element: Element) => target === element); + const over = vi.fn(); instance.controller.set({ matchTarget, over }); @@ -877,13 +885,13 @@ describe('Drag controller', () => { await elementUpdated(instance); // Not called on initial drag enter - expect(over.called).is.false; + expect(over).not.toHaveBeenCalled(); // 5 pointer moves over the matched element simulatePointerMove(instance, { clientX, clientY }, { x: 5, y: 5 }, 5); await elementUpdated(instance); - expect(over.callCount).to.equal(5); + expect(over).toHaveBeenCalledTimes(5); expect(getCallbackArgs(over).state.element).to.eql(target); }); }); diff --git a/src/components/common/controllers/focus-ring.spec.ts b/src/components/common/controllers/focus-ring.spec.ts index cb3a9fb05..8232a63a1 100644 --- a/src/components/common/controllers/focus-ring.spec.ts +++ b/src/components/common/controllers/focus-ring.spec.ts @@ -1,12 +1,12 @@ +import { css, LitElement } from 'lit'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { defineCE, elementUpdated, - expect, fixture, html, unsafeStatic, -} from '@open-wc/testing'; -import { css, LitElement } from 'lit'; +} from '../../common/helpers.spec.js'; import { partMap } from '../part-map.js'; import { simulateClick, @@ -21,7 +21,7 @@ describe('Focus ring controller', () => { let tag: string; let instance: LitElement & { button: HTMLButtonElement }; - before(() => { + beforeAll(() => { tag = defineCE( class extends LitElement { public static override styles = css` diff --git a/src/components/common/controllers/gestures.spec.ts b/src/components/common/controllers/gestures.spec.ts index b616fab99..2b69e895c 100644 --- a/src/components/common/controllers/gestures.spec.ts +++ b/src/components/common/controllers/gestures.spec.ts @@ -1,12 +1,14 @@ +import { css, LitElement } from 'lit'; import { - defineCE, + afterEach, + beforeAll, + beforeEach, + describe, expect, - fixture, - html, - unsafeStatic, -} from '@open-wc/testing'; -import { css, LitElement } from 'lit'; -import { type SinonFakeTimers, useFakeTimers } from 'sinon'; + it, + vi, +} from 'vitest'; +import { defineCE, fixture, html, unsafeStatic } from '../helpers.spec.js'; import { simulateLostPointerCapture, simulatePointerDown, @@ -15,14 +17,13 @@ import { import { addGesturesController, type SwipeEvent } from './gestures.js'; describe('Gestures controller', () => { - let clock: SinonFakeTimers; let tag: string; let instance: LitElement & { gestures: ReturnType; events: SwipeEvent[]; }; - before(() => { + beforeAll(() => { tag = defineCE( class extends LitElement { public static override styles = css` @@ -64,10 +65,10 @@ describe('Gestures controller', () => { }); describe('Options', () => { - afterEach(() => clock.restore()); + afterEach(() => vi.useRealTimers()); beforeEach(async () => { - clock = useFakeTimers({ toFake: ['Date'] }); + vi.useFakeTimers({ toFake: ['Date'] }); }); it('correct default options', () => { @@ -97,14 +98,14 @@ describe('Gestures controller', () => { it('correctly takes `thresholdTime` into account', () => { instance.gestures.updateOptions({ thresholdTime: 350 }); simulatePointerDown(instance); - clock.tick(500); + vi.advanceTimersByTime(500); simulatePointerMove(instance, {}, { x: 250 }); simulateLostPointerCapture(instance); expect(instance.events).lengthOf(0); simulatePointerDown(instance); - clock.tick(350); + vi.advanceTimersByTime(350); simulatePointerMove(instance, {}, { x: 250 }); simulateLostPointerCapture(instance); diff --git a/src/components/common/controllers/key-bindings.spec.ts b/src/components/common/controllers/key-bindings.spec.ts index aa6d1eaf3..9c8532d95 100644 --- a/src/components/common/controllers/key-bindings.spec.ts +++ b/src/components/common/controllers/key-bindings.spec.ts @@ -1,11 +1,6 @@ -import { - defineCE, - expect, - fixture, - html, - unsafeStatic, -} from '@open-wc/testing'; import { LitElement } from 'lit'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import { defineCE, fixture, html, unsafeStatic } from '../helpers.spec.js'; import { simulateKeyboard } from '../utils.spec.js'; import { addKeybindings, @@ -24,7 +19,7 @@ describe('Key bindings controller', () => { input: HTMLInputElement; }; - before(() => { + beforeAll(() => { tag = defineCE( class extends LitElement { public key?: string; diff --git a/src/components/common/controllers/slot.spec.ts b/src/components/common/controllers/slot.spec.ts index 263258f24..8c6971831 100644 --- a/src/components/common/controllers/slot.spec.ts +++ b/src/components/common/controllers/slot.spec.ts @@ -1,12 +1,12 @@ +import { LitElement } from 'lit'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { defineCE, elementUpdated, - expect, fixture, html, unsafeStatic, -} from '@open-wc/testing'; -import { LitElement } from 'lit'; +} from '../helpers.spec.js'; import { first, last } from '../util.js'; import { addSlotController, @@ -27,7 +27,7 @@ describe('Slots controller', () => { }; let slots: TestSlotController; - before(() => { + beforeAll(() => { tag = defineCE( class extends LitElement { public readonly slotsCallbackResults: SlotChangeCallbackParameters< @@ -162,11 +162,9 @@ describe('Slots controller', () => { describe('Slot onChange callback', () => { beforeEach(async () => { - beforeEach(async () => { - const tagName = unsafeStatic(tag); - instance = await fixture(html`<${tagName}> { diff --git a/src/components/common/equal.spec.ts b/src/components/common/equal.spec.ts index 10ea4e3be..0fc9ed91f 100644 --- a/src/components/common/equal.spec.ts +++ b/src/components/common/equal.spec.ts @@ -1,4 +1,4 @@ -import { expect } from '@open-wc/testing'; +import { describe, expect, it } from 'vitest'; import { equal } from './util.js'; describe('equal', () => { diff --git a/src/components/common/helpers.spec.ts b/src/components/common/helpers.spec.ts new file mode 100644 index 000000000..e59fbd28f --- /dev/null +++ b/src/components/common/helpers.spec.ts @@ -0,0 +1,145 @@ +import { type RenderOptions, render, type TemplateResult } from 'lit'; +import { html, unsafeStatic } from 'lit/static-html.js'; +import { beforeEach } from 'vitest'; +import { page } from 'vitest/browser'; +import type { Constructor } from './mixins/constructor.js'; + +interface LitTemplateResult { + processor: any; + strings: TemplateStringsArray; + type: string; + values: readonly unknown[]; +} + +type RenderResult = + | LitTemplateResult + | TemplateResult + | TemplateResult[] + | Node + | Node[] + | string + | string[] + | number + | number[] + | boolean + | boolean[] + | null + | undefined; + +type FixtureOptions = { + container?: HTMLElement; + options?: RenderOptions; +}; + +let defineCECounter = 0; +const containers = new Set(); + +export async function fixture( + template: RenderResult, + options: FixtureOptions = {} +) { + const container = + options?.container ?? + document.body.appendChild(document.createElement('div')); + + containers.add(container); + render(template, container, options.options); + + const element = container.firstElementChild as T; + await elementUpdated(element); + + return element; +} + +/** Clean up all rendered fixtures */ +export function cleanup(): void { + for (const container of containers) { + container.remove(); + } + containers.clear(); +} + +/** Define a custom element with a unique tag name for testing */ +export function defineCE( + _class: Constructor +): string { + const uniqueTagName = `test-${defineCECounter}`; + customElements.define(uniqueTagName, _class); + defineCECounter += 1; + return uniqueTagName; +} + +/** Wait for the next animation frame */ +export async function nextFrame(): Promise { + return new Promise((resolve) => requestAnimationFrame(() => resolve())); +} + +/** Wait for a specific amount of time */ +export function aTimeout(timeout: number): Promise { + return new Promise((resolve) => setTimeout(() => resolve(), timeout)); +} + +/** Wait for an element to finish updating */ +export async function elementUpdated( + element: T +): Promise { + let hasSpecificUpdate = false; + + if (element && 'updateComplete' in element) { + await element.updateComplete; + hasSpecificUpdate = true; + } + + if (!hasSpecificUpdate) { + await nextFrame(); + } + + return element; +} + +/** Wait until a predicate is true or a timeout occurs */ +export function waitUntil( + predicate: () => unknown | Promise, + message?: string, + options: { interval?: number; timeout?: number } = {} +): Promise { + const { interval = 50, timeout = 1000 } = options; + const { stack } = new Error(); + + return new Promise((resolve, reject) => { + let timeoutId: ReturnType | undefined; + + setTimeout(() => { + clearTimeout(timeoutId); + + const error = new Error( + message + ? `Timeout: ${message}` + : `waitUntil timed out after ${timeout}ms` + ); + error.stack = stack ?? ''; + reject(error); + }, timeout); + + async function nextInterval() { + try { + if (await predicate()) { + resolve(); + } else { + timeoutId = setTimeout(() => { + nextInterval(); + }, interval); + } + } catch (error) { + reject(error); + } + } + + nextInterval(); + }); +} + +page.extend({ fixture, [Symbol.for('vitest:component-cleanup')]: cleanup }); +beforeEach(() => cleanup()); + +export { html, unsafeStatic }; diff --git a/src/components/common/i18n/i18n.spec.ts b/src/components/common/i18n/i18n.spec.ts index 2d622e89c..05fbc0d50 100644 --- a/src/components/common/i18n/i18n.spec.ts +++ b/src/components/common/i18n/i18n.spec.ts @@ -1,11 +1,3 @@ -import { - defineCE, - elementUpdated, - expect, - fixture, - html, - unsafeStatic, -} from '@open-wc/testing'; import { ComboResourceStringsEN, getI18nManager, @@ -16,6 +8,14 @@ import { } from 'igniteui-i18n-core'; import { ResourceStringsBG } from 'igniteui-i18n-resources'; import { LitElement } from 'lit'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import { + defineCE, + elementUpdated, + fixture, + html, + unsafeStatic, +} from '../helpers.spec.js'; import { type IgcDateRangePickerResourceStrings, IgcDateRangePickerResourceStringsEN, @@ -56,7 +56,7 @@ describe('Localization', () => { i18nController: I18nController; }; - before(() => { + beforeAll(() => { tagOld = defineCE( class extends TestLocalizedClass { public override get defaultEN() { diff --git a/src/components/common/mixins/form-associated.spec.ts b/src/components/common/mixins/form-associated.spec.ts index fded52c85..dadb8cb5d 100644 --- a/src/components/common/mixins/form-associated.spec.ts +++ b/src/components/common/mixins/form-associated.spec.ts @@ -1,13 +1,8 @@ -import { - defineCE, - expect, - fixture, - html, - unsafeStatic, -} from '@open-wc/testing'; import { LitElement } from 'lit'; import { ifDefined } from 'lit/directives/if-defined.js'; import type { StaticValue } from 'lit/static-html.js'; +import { beforeAll, describe, expect, it } from 'vitest'; +import { defineCE, fixture, html, unsafeStatic } from '../helpers.spec.js'; import { maxLengthValidator, minLengthValidator, @@ -39,7 +34,7 @@ describe('Form associated mixin tests', () => { let tagName: StaticValue; let instance: FormAssociatedTestInstance; - before(() => { + beforeAll(() => { tag = defineCE( class Foo extends FormAssociatedRequiredMixin(LitElement) { static override properties = { diff --git a/src/components/common/utils.spec.ts b/src/components/common/utils.spec.ts index 168267606..9af65323d 100644 --- a/src/components/common/utils.spec.ts +++ b/src/components/common/utils.spec.ts @@ -1,15 +1,10 @@ -import { - elementUpdated, - expect, - fixture, - html, - nextFrame, -} from '@open-wc/testing'; -import type { TemplateResult } from 'lit'; +import { html, type TemplateResult } from 'lit'; +import { expect, type MockInstance } from 'vitest'; import { type CalendarDay, toCalendarDay } from '../calendar/model.js'; import { parseKeys } from './controllers/key-bindings.js'; +import { elementUpdated, fixture, nextFrame } from './helpers.spec.js'; import type { IgcFormControl } from './mixins/forms/types.js'; -import { toKebabCase } from './util.js'; +import { equal, toKebabCase } from './util.js'; export function createFormAssociatedTestBed( template: TemplateResult @@ -464,3 +459,55 @@ export function compareStyles( export function checkDatesEqual(a: CalendarDay | Date, b: CalendarDay | Date) { expect(toCalendarDay(a).equalTo(toCalendarDay(b))).to.be.true; } + +/** * Checks whether a spy was called with a specific event name. */ +export function eventMatch(spy: MockInstance, eventName: string): boolean { + return spy.mock.calls.some((call: unknown[]) => call[0] === eventName); +} + +/** * Checks whether a spy was called with a specific event name and arguments. */ +export function eventArgsMatch( + spy: MockInstance, + eventName: string, + args: unknown +): boolean { + return spy.mock.calls.some((call: unknown[]) => { + return call[0] === eventName && equal(call[1], args); + }); +} + +/** * Returns all calls of a spy with a specific event name. */ +export function getEvents(spy: MockInstance, eventName: string): any[] { + return spy.mock.calls.filter((call: unknown[]) => call[0] === eventName); +} + +/** * Expects a spy to have been called with a specific event name. */ +export function expectCalledWith(spy: MockInstance, eventName: string): void { + expect(eventMatch(spy, eventName)).to.be.true; +} + +/** * Expects a spy to not have been called with a specific event name. */ +export function expectNotCalledWith( + spy: MockInstance, + eventName: string +): void { + expect(eventMatch(spy, eventName)).to.be.false; +} + +/** * Expects a spy to have been called with a specific event name and arguments. */ +export function expectCalledWithArgs( + spy: MockInstance, + eventName: string, + args: unknown +): void { + expect(eventArgsMatch(spy, eventName, args)).to.be.true; +} + +/** * Expects a spy to not have been called with a specific event name and arguments. */ +export function expectNotCalledWithArgs( + spy: MockInstance, + eventName: string, + args: unknown +): void { + expect(eventArgsMatch(spy, eventName, args)).to.be.false; +} diff --git a/src/components/common/validity-helpers.spec.ts b/src/components/common/validity-helpers.spec.ts index 649372d8c..5cf88bf6e 100644 --- a/src/components/common/validity-helpers.spec.ts +++ b/src/components/common/validity-helpers.spec.ts @@ -1,6 +1,7 @@ -import { elementUpdated, expect } from '@open-wc/testing'; +import { expect } from 'vitest'; import IgcValidationContainerComponent from '../validation-container/validation-container.js'; import type { IgniteComponent } from './definitions/register.js'; +import { elementUpdated } from './helpers.spec.js'; import type { Constructor } from './mixins/constructor.js'; import type { IgcFormControl } from './mixins/forms/types.js'; import { isEmpty, toKebabCase } from './util.js'; diff --git a/src/components/date-picker/date-picker-form.spec.ts b/src/components/date-picker/date-picker-form.spec.ts index 404ed710c..68a5224c1 100644 --- a/src/components/date-picker/date-picker-form.spec.ts +++ b/src/components/date-picker/date-picker-form.spec.ts @@ -1,7 +1,8 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { CalendarDay, toCalendarDay } from '../calendar/model.js'; import { type DateRangeDescriptor, DateRangeType } from '../calendar/types.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { equal } from '../common/util.js'; import { createFormAssociatedTestBed, @@ -16,7 +17,7 @@ import IgcDateTimeInputComponent from '../date-time-input/date-time-input.js'; import IgcDatePickerComponent from './date-picker.js'; describe('igc-datepicker form integration', () => { - before(() => defineComponents(IgcDatePickerComponent)); + beforeAll(() => defineComponents(IgcDatePickerComponent)); function checkDatesEqual(a: CalendarDay | Date, b: CalendarDay | Date) { expect(equal(toCalendarDay(a), toCalendarDay(b))).to.be.true; diff --git a/src/components/date-picker/date-picker.spec.ts b/src/components/date-picker/date-picker.spec.ts index 9f0b8e3d7..fc1454b15 100644 --- a/src/components/date-picker/date-picker.spec.ts +++ b/src/components/date-picker/date-picker.spec.ts @@ -1,5 +1,4 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import { spy } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import IgcCalendarComponent from '../calendar/calendar.js'; import { getCalendarDOM, @@ -15,8 +14,11 @@ import { escapeKey, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { equal } from '../common/util.js'; import { + expectCalledWith, + expectNotCalledWith, isFocused, simulateClick, simulateKeyboard, @@ -25,7 +27,7 @@ import IgcDateTimeInputComponent from '../date-time-input/date-time-input.js'; import IgcDatePickerComponent from './date-picker.js'; describe('Date picker', () => { - before(() => defineComponents(IgcDatePickerComponent)); + beforeAll(() => defineComponents(IgcDatePickerComponent)); const pickerShowIcon = 'today'; const pickerClearIcon = 'input_clear'; @@ -307,17 +309,17 @@ describe('Date picker', () => { it('should show/hide the picker based on the value of the open attribute', async () => { expect(picker.open).to.equal(false); picker.open = true; - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); await elementUpdated(picker); expect(picker.open).to.equal(true); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); picker.open = false; await elementUpdated(picker); expect(picker.open).to.equal(false); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); }); it('should set prompt char correctly', async () => { @@ -334,12 +336,12 @@ describe('Date picker', () => { await picker.show(); - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); selectCurrentDate(calendar); await elementUpdated(picker); - expect(eventSpy).calledOnce; + expect(spy).toHaveBeenCalledTimes(1); checkDatesEqual(picker.value as Date, currentDate); checkDatesEqual(calendar.value as Date, currentDate); @@ -369,19 +371,19 @@ describe('Date picker', () => { await picker.show(); - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); selectCurrentDate(calendar); await elementUpdated(picker); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkDatesEqual(picker.value as Date, currentDate); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(dateTimeInput, '1'); await elementUpdated(picker); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); checkDatesEqual(picker.value as Date, currentDate); }); @@ -394,22 +396,22 @@ describe('Date picker', () => { await picker.show(); - const eventSpy = spy(picker, 'emitEvent'); - const calendarEventSpy = spy(calendar, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); + const calendarEventSpy = vi.spyOn(calendar, 'emitEvent'); selectCurrentDate(calendar); await elementUpdated(picker); - expect(eventSpy).not.calledWith('igcChange'); - expect(calendarEventSpy).calledWith('igcChange'); + expect(spy).not.toHaveBeenCalledWith('igcChange'); + expectCalledWith(calendarEventSpy, 'igcChange'); checkDatesEqual(picker.value as Date, tomorrowDate); checkDatesEqual(calendar.value as Date, tomorrowDate); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(dateTimeInput, '1'); await elementUpdated(picker); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); checkDatesEqual(picker.value as Date, tomorrowDate); }); @@ -569,7 +571,7 @@ describe('Date picker', () => { it('should default inputFormat to whatever Intl.DateTimeFormat returns for the current locale', async () => { const defaultFormat = 'MM/dd/yyyy'; - expect(picker.locale).to.equal('en-US'); + expect(picker.locale).to.equal('en'); expect(picker.inputFormat).to.equal(defaultFormat); picker.locale = 'fr'; @@ -579,7 +581,7 @@ describe('Date picker', () => { }); it('should use the value of locale format for displayFormat, if it is not defined', async () => { - expect(picker.locale).to.equal('en-US'); + expect(picker.locale).to.equal('en'); expect(picker.getAttribute('display-format')).to.be.null; expect(picker.displayFormat).to.equal('M/d/yyyy'); @@ -624,27 +626,27 @@ describe('Date picker', () => { }); it('should open/close the picker on invoking show/hide/toggle and not emit events', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); expect(picker.open).to.be.false; await picker.show(); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.open).to.be.true; await picker.hide(); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.open).to.be.false; await picker.toggle(); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.open).to.be.true; await picker.toggle(); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.open).to.be.false; }); @@ -661,22 +663,22 @@ describe('Date picker', () => { }); it('should delegate stepUp and stepDown to the igc-date-time-input', async () => { - const stepUpSpy = spy(dateTimeInput, 'stepUp'); - const stepDownSpy = spy(dateTimeInput, 'stepDown'); + const stepUpSpy = vi.spyOn(dateTimeInput, 'stepUp'); + const stepDownSpy = vi.spyOn(dateTimeInput, 'stepDown'); picker.stepUp(); await elementUpdated(picker); - expect(stepUpSpy).called; + expect(stepUpSpy).toHaveBeenCalled(); picker.stepDown(); await elementUpdated(picker); - expect(stepDownSpy).called; + expect(stepDownSpy).toHaveBeenCalled(); }); it('should select the text in the input with the select method', async () => { - const selectSpy = spy(dateTimeInput, 'select'); + const selectSpy = vi.spyOn(dateTimeInput, 'select'); picker.value = new Date(); await elementUpdated(picker); @@ -685,13 +687,13 @@ describe('Date picker', () => { picker.select(); await elementUpdated(picker); - expect(selectSpy).to.be.called; + expect(selectSpy).toHaveBeenCalled(); expect(input.selectionStart).to.eq(0); expect(input.selectionEnd).to.eq(input.value.length); }); it('should set the text selection range in the input with setSelectionRange()', async () => { - const selectionRangeSpy = spy(dateTimeInput, 'setSelectionRange'); + const selectionRangeSpy = vi.spyOn(dateTimeInput, 'setSelectionRange'); picker.value = new Date(); await elementUpdated(picker); @@ -700,13 +702,13 @@ describe('Date picker', () => { picker.setSelectionRange(0, 2); await elementUpdated(picker); - expect(selectionRangeSpy).to.be.called; + expect(selectionRangeSpy).toHaveBeenCalled(); expect(input.selectionStart).to.eq(0); expect(input.selectionEnd).to.eq(2); }); it('should replace the selected text in the input and re-apply the mask with setRangeText()', async () => { - const setRangeTextSpy = spy(dateTimeInput, 'setRangeText'); + const setRangeTextSpy = vi.spyOn(dateTimeInput, 'setRangeText'); picker.value = new Date(2024, 2, 21); const expectedValue = new Date(2023, 2, 21); await elementUpdated(picker); @@ -715,7 +717,7 @@ describe('Date picker', () => { picker.setRangeText('2023', 6, 10); await elementUpdated(picker); - expect(setRangeTextSpy).to.be.called; + expect(setRangeTextSpy).toHaveBeenCalled(); checkDatesEqual(new Date(input.value), expectedValue); checkDatesEqual(picker.value!, expectedValue); @@ -725,22 +727,22 @@ describe('Date picker', () => { describe('Interactions', () => { it('should close the picker when in open state on pressing Escape', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const eventSpy = vi.spyOn(picker, 'emitEvent'); picker.focus(); simulateKeyboard(picker, escapeKey); await elementUpdated(picker); - expect(eventSpy).not.called; + expect(eventSpy).not.toHaveBeenCalled(); await picker.show(); simulateKeyboard(picker, escapeKey); await elementUpdated(picker); - expect(eventSpy).calledTwice; - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).calledWith('igcClosed'); - eventSpy.resetHistory(); + expect(eventSpy).toHaveBeenCalledTimes(2); + expectCalledWith(eventSpy, 'igcClosing'); + expectCalledWith(eventSpy, 'igcClosed'); + eventSpy.mockClear(); // dialog mode picker.mode = 'dialog'; @@ -749,13 +751,13 @@ describe('Date picker', () => { simulateKeyboard(picker, escapeKey); await elementUpdated(picker); - expect(eventSpy).calledTwice; - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).calledWith('igcClosed'); + expect(eventSpy).toHaveBeenCalledTimes(2); + expectCalledWith(eventSpy, 'igcClosing'); + expectCalledWith(eventSpy, 'igcClosed'); }); it('should open the calendar picker on Alt + ArrowDown and close it on Alt + ArrowUp - dropdown mode', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const eventSpy = vi.spyOn(picker, 'emitEvent'); expect(picker.open).to.be.false; picker.focus(); simulateKeyboard(picker, [altKey, arrowDown]); @@ -763,22 +765,21 @@ describe('Date picker', () => { expect(picker.open).to.be.true; - expect(eventSpy).calledWith('igcOpening'); - expect(eventSpy).calledWith('igcOpened'); - - eventSpy.resetHistory(); + expect(eventSpy).toHaveBeenCalledWith('igcOpening', { cancelable: true }); + expect(eventSpy).toHaveBeenCalledWith('igcOpened'); + eventSpy.mockClear(); simulateKeyboard(picker, [altKey, arrowUp]); await elementUpdated(picker); expect(picker.open).to.be.false; - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).calledWith('igcClosed'); - eventSpy.resetHistory(); + expect(eventSpy).toHaveBeenCalledWith('igcClosing', { cancelable: true }); + expect(eventSpy).toHaveBeenCalledWith('igcClosed'); + eventSpy.mockClear(); }); it('should open the calendar picker on Alt + ArrowDown and close it on Alt + ArrowUp - dialog mode', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const eventSpy = vi.spyOn(picker, 'emitEvent'); expect(picker.open).to.be.false; picker.focus(); picker.mode = 'dialog'; @@ -791,28 +792,28 @@ describe('Date picker', () => { expect(picker.open).to.be.true; expect(dialog).not.to.be.undefined; expect(dialog?.open).to.be.true; - expect(eventSpy).calledWith('igcOpening'); - expect(eventSpy).calledWith('igcOpened'); - eventSpy.resetHistory(); + expect(eventSpy).toHaveBeenCalledWith('igcOpening', { cancelable: true }); + expect(eventSpy).toHaveBeenCalledWith('igcOpened'); + eventSpy.mockClear(); simulateKeyboard(picker, [altKey, arrowUp]); await elementUpdated(picker); expect(picker.open).to.be.false; - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).calledWith('igcClosed'); + expect(eventSpy).toHaveBeenCalledWith('igcClosing', { cancelable: true }); + expect(eventSpy).toHaveBeenCalledWith('igcClosed'); }); it('should emit or not igcInput according to nonEditable property', async () => { const expectedValue = new Date(); - const eventSpy = spy(picker, 'emitEvent'); + const eventSpy = vi.spyOn(picker, 'emitEvent'); dateTimeInput.focus(); simulateKeyboard(dateTimeInput, arrowUp); await elementUpdated(picker); - expect(eventSpy).calledOnceWith('igcInput'); - eventSpy.resetHistory(); + expectCalledWith(eventSpy, 'igcInput'); + eventSpy.mockClear(); checkDatesEqual(picker.value as Date, expectedValue); picker.value = null; @@ -823,7 +824,7 @@ describe('Date picker', () => { simulateKeyboard(dateTimeInput, arrowUp); await elementUpdated(picker); - expect(eventSpy).not.called; + expect(eventSpy).not.toHaveBeenCalled(); expect(picker.value).to.be.null; dateTimeInput.dispatchEvent( @@ -831,7 +832,7 @@ describe('Date picker', () => { ); await elementUpdated(picker); - expect(eventSpy).not.called; + expect(eventSpy).not.toHaveBeenCalled(); expect(picker.value).to.be.null; }); @@ -951,7 +952,7 @@ describe('Date picker', () => { }); it('should update the calendar view when typing, i.e. switch to other month', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const eventSpy = vi.spyOn(picker, 'emitEvent'); const date = new CalendarDay({ year: 2025, month: 1, date: 1 }); picker.value = date.native; picker.open = true; @@ -967,8 +968,8 @@ describe('Date picker', () => { await elementUpdated(picker); - expect(eventSpy).calledWith('igcInput'); - expect(eventSpy).not.calledWith('igcChange'); + expectCalledWith(eventSpy, 'igcInput'); + expectNotCalledWith(eventSpy, 'igcChange'); const expectedValue = new CalendarDay({ year: 2025, month: 3, date: 1 }) .native; @@ -976,7 +977,7 @@ describe('Date picker', () => { }); it('issue 1884 - should emit igcChange event in dialog mode after clearing the value and losing focus', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const eventSpy = vi.spyOn(picker, 'emitEvent'); // Dropdown mode @@ -994,11 +995,11 @@ describe('Date picker', () => { picker.blur(); expect(isFocused(dateTimeInput)).to.be.false; - expect(eventSpy).to.be.calledWith('igcChange', { + expect(eventSpy).toHaveBeenCalledWith('igcChange', { detail: null, }); - eventSpy.resetHistory(); + eventSpy.mockClear(); // Dialog mode picker.mode = 'dialog'; @@ -1016,7 +1017,7 @@ describe('Date picker', () => { picker.blur(); expect(isFocused(dateTimeInput)).to.be.false; - expect(eventSpy).to.be.calledWith('igcChange', { + expect(eventSpy).toHaveBeenCalledWith('igcChange', { detail: null, }); }); diff --git a/src/components/date-range-picker/date-range-picker-single.form.spec.ts b/src/components/date-range-picker/date-range-picker-single.form.spec.ts index 77d592d4a..316f2e00b 100644 --- a/src/components/date-range-picker/date-range-picker-single.form.spec.ts +++ b/src/components/date-range-picker/date-range-picker-single.form.spec.ts @@ -1,7 +1,8 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { CalendarDay } from '../calendar/model.js'; import { type DateRangeDescriptor, DateRangeType } from '../calendar/types.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, simulateClick, @@ -18,7 +19,7 @@ import IgcDateRangePickerComponent, { import { checkSelectedRange, getIcon } from './date-range-picker.utils.spec.js'; describe('Date Range Picker Single Input - Form integration', () => { - before(() => defineComponents(IgcDateRangePickerComponent)); + beforeAll(() => defineComponents(IgcDateRangePickerComponent)); let picker: IgcDateRangePickerComponent; let input: IgcInputComponent; diff --git a/src/components/date-range-picker/date-range-picker-single.spec.ts b/src/components/date-range-picker/date-range-picker-single.spec.ts index 8d79c7dd8..9c8030811 100644 --- a/src/components/date-range-picker/date-range-picker-single.spec.ts +++ b/src/components/date-range-picker/date-range-picker-single.spec.ts @@ -1,11 +1,4 @@ -import { - elementUpdated, - expect, - fixture, - html, - waitUntil, -} from '@open-wc/testing'; -import { spy } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import IgcCalendarComponent from '../calendar/calendar.js'; import { CalendarDay } from '../calendar/model.js'; import { @@ -16,6 +9,15 @@ import { } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; import { + elementUpdated, + fixture, + html, + waitUntil, +} from '../common/helpers.spec.js'; +import { + eventMatch, + expectCalledWith, + expectNotCalledWith, isFocused, simulateClick, simulateKeyboard, @@ -30,7 +32,7 @@ import { } from './date-range-picker.utils.spec.js'; describe('Date range picker - single input', () => { - before(() => defineComponents(IgcDateRangePickerComponent)); + beforeAll(() => defineComponents(IgcDateRangePickerComponent)); let picker: IgcDateRangePickerComponent; let input: IgcInputComponent; @@ -132,7 +134,7 @@ describe('Date range picker - single input', () => { }); it('should modify value only through calendar selection and not input', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); // current implementation of DRP single input is not editable; // to refactor when the input is made editable //picker.nonEditable = true; @@ -146,7 +148,7 @@ describe('Date range picker - single input', () => { expect(input.value).to.equal(''); expect(picker.value).to.deep.equal({ start: null, end: null }); expect(calendar.values).to.deep.equal([]); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); await picker.show(); await selectDates(today, tomorrow, calendar); @@ -155,7 +157,7 @@ describe('Date range picker - single input', () => { { start: today.native, end: tomorrow.native }, false ); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); }); it('should set properties of the input correctly', async () => { @@ -257,7 +259,7 @@ describe('Date range picker - single input', () => { }); describe('Methods', () => { it('should clear the input on invoking clear()', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.value = { start: CalendarDay.from(new Date(2025, 3, 9)).native, end: CalendarDay.from(new Date(2025, 3, 10)).native, @@ -269,12 +271,12 @@ describe('Date range picker - single input', () => { picker.clear(); await elementUpdated(picker); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.value).to.deep.equal(null); expect(input.value).to.equal(''); }); it('should select a date range on invoking select', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); expect(picker.value).to.deep.equal({ start: null, end: null }); const start = CalendarDay.from(new Date(2025, 3, 9)).native; const end = CalendarDay.from(new Date(2025, 3, 10)).native; @@ -289,18 +291,18 @@ describe('Date range picker - single input', () => { end, }); expect(input.value).to.equal('4/9/2025 - 4/10/2025'); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); }); }); describe('Interactions', () => { describe('Selection via the calendar', () => { it('should select a single date in dropdown mode and emit igcChange', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.open = true; await elementUpdated(picker); await selectDates(today, null, calendar); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkSelectedRange( picker, { start: today.native, end: today.native }, @@ -313,13 +315,13 @@ describe('Date range picker - single input', () => { }); it('should select a range of dates in dropdown mode and emit igcChange', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.open = true; await elementUpdated(picker); await selectDates(today, tomorrow, calendar); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkSelectedRange( picker, { @@ -335,7 +337,7 @@ describe('Date range picker - single input', () => { }); it('should select a range of dates in dialog mode and emit igcChange when done is clicked', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; await elementUpdated(picker); @@ -344,7 +346,7 @@ describe('Date range picker - single input', () => { await elementUpdated(picker); await selectDates(today, tomorrow, calendar); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); let dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(true); @@ -353,7 +355,7 @@ describe('Date range picker - single input', () => { ) as HTMLButtonElement; doneBtn?.click(); await elementUpdated(picker); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkSelectedRange( picker, { @@ -368,7 +370,7 @@ describe('Date range picker - single input', () => { }); it('should select a range of dates in dialog mode and emit igcChange when clicked outside of dialog', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; await elementUpdated(picker); @@ -377,7 +379,7 @@ describe('Date range picker - single input', () => { await elementUpdated(picker); await selectDates(today, tomorrow, calendar); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); let dialog = picker.renderRoot.querySelector( 'igc-dialog' ) as IgcDialogComponent; @@ -388,10 +390,10 @@ describe('Date range picker - single input', () => { simulateClick(nativeDialog, { clientX: x + 1, clientY: y - 1 }); await elementUpdated(dialog); - await waitUntil(() => eventSpy.calledWith('igcClosed')); + await waitUntil(() => eventMatch(spy, 'igcClosed')); await elementUpdated(picker); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkSelectedRange( picker, { @@ -408,7 +410,7 @@ describe('Date range picker - single input', () => { }); it('should not emit igcChange when cancel is clicked and the value should be the initial value', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; const date1 = new CalendarDay({ @@ -428,7 +430,7 @@ describe('Date range picker - single input', () => { await elementUpdated(picker); await selectDates(date1, date2, calendar); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); let dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(true); checkSelectedRange( @@ -442,7 +444,7 @@ describe('Date range picker - single input', () => { ) as HTMLButtonElement; cancelBtn?.click(); await elementUpdated(picker); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(false); checkSelectedRange( @@ -456,7 +458,7 @@ describe('Date range picker - single input', () => { }); it('should revert to no value when cancel is clicked and initial value is null', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; const date1 = new CalendarDay({ @@ -476,7 +478,7 @@ describe('Date range picker - single input', () => { await elementUpdated(picker); await selectDates(date1, date2, calendar); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); let dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(true); checkSelectedRange( @@ -490,7 +492,7 @@ describe('Date range picker - single input', () => { ) as HTMLButtonElement; cancelBtn?.click(); await elementUpdated(picker); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(false); @@ -504,7 +506,7 @@ describe('Date range picker - single input', () => { }); it('should not emit igcChange when escape is pressed and the value should be the initial value', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; const date1 = new CalendarDay({ @@ -524,7 +526,7 @@ describe('Date range picker - single input', () => { await elementUpdated(picker); await selectDates(date1, date2, calendar); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); let dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(true); checkSelectedRange( @@ -536,7 +538,7 @@ describe('Date range picker - single input', () => { simulateKeyboard(picker, escapeKey); await elementUpdated(picker); await elementUpdated(input); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(false); @@ -570,7 +572,7 @@ describe('Date range picker - single input', () => { }); it('should clear the inputs on clicking the clear icon', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.useTwoInputs = false; picker.value = { start: today.native, end: tomorrow.native }; await elementUpdated(picker); @@ -586,7 +588,7 @@ describe('Date range picker - single input', () => { await elementUpdated(input); expect(isFocused(input)).to.be.false; - expect(eventSpy).to.be.calledWith('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: null, }); expect(picker.open).to.be.false; @@ -595,7 +597,7 @@ describe('Date range picker - single input', () => { }); it('should toggle the calendar with keyboard combinations and keep focus', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); const input = picker.renderRoot!.querySelector( IgcInputComponent.tagName )!; @@ -608,22 +610,26 @@ describe('Date range picker - single input', () => { expect(picker.open).to.be.true; expect(isFocused(input)).to.be.false; - expect(eventSpy.firstCall).calledWith('igcOpening'); - expect(eventSpy.lastCall).calledWith('igcOpened'); - eventSpy.resetHistory(); + expect(spy).toHaveBeenNthCalledWith(1, 'igcOpening', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcOpened'); + spy.mockClear(); simulateKeyboard(input, [altKey, arrowUp]); await elementUpdated(picker); expect(picker.open).to.be.false; expect(isFocused(input)).to.be.true; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.lastCall).calledWith('igcClosed'); - eventSpy.resetHistory(); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosed'); + spy.mockClear(); simulateKeyboard(input, [altKey, arrowDown]); await elementUpdated(picker); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(picker, escapeKey); await elementUpdated(picker); @@ -631,8 +637,10 @@ describe('Date range picker - single input', () => { expect(picker.open).to.be.false; expect(isFocused(input)).to.be.true; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.lastCall).calledWith('igcClosed'); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosed'); }); }); @@ -644,17 +652,17 @@ describe('Date range picker - single input', () => { await elementUpdated(picker); }); it('should not clear the input(s) via the clear icon when readOnly is true', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); simulateClick(getIcon(picker, clearIcon)); await elementUpdated(picker); expect(picker.value).to.deep.equal(testValue); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); checkSelectedRange(picker, testValue, false); }); it('should not open the calendar on clicking the input - dropdown mode', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); const igcInput = picker.renderRoot.querySelector( IgcInputComponent.tagName )!; @@ -663,10 +671,10 @@ describe('Date range picker - single input', () => { await elementUpdated(picker); expect(picker.open).to.be.false; - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); }); it('should not open the calendar on clicking the input - dialog mode', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; await elementUpdated(picker); const igcInput = picker.renderRoot.querySelector( @@ -678,7 +686,7 @@ describe('Date range picker - single input', () => { await elementUpdated(picker); expect(picker.open).to.be.false; - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); }); }); }); diff --git a/src/components/date-range-picker/date-range-picker-two-inputs.form.spec.ts b/src/components/date-range-picker/date-range-picker-two-inputs.form.spec.ts index 259e56d0f..67a814a08 100644 --- a/src/components/date-range-picker/date-range-picker-two-inputs.form.spec.ts +++ b/src/components/date-range-picker/date-range-picker-two-inputs.form.spec.ts @@ -1,7 +1,8 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { CalendarDay } from '../calendar/model.js'; import { type DateRangeDescriptor, DateRangeType } from '../calendar/types.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, simulateClick, @@ -24,7 +25,7 @@ import { } from './date-range-picker.utils.spec.js'; describe('Date Range Picker Two Inputs - Form integration', () => { - before(() => defineComponents(IgcDateRangePickerComponent)); + beforeAll(() => defineComponents(IgcDateRangePickerComponent)); let picker: IgcDateRangePickerComponent; let dateTimeInputs: IgcDateTimeInputComponent[]; diff --git a/src/components/date-range-picker/date-range-picker-two-inputs.spec.ts b/src/components/date-range-picker/date-range-picker-two-inputs.spec.ts index 27d721050..8557407bd 100644 --- a/src/components/date-range-picker/date-range-picker-two-inputs.spec.ts +++ b/src/components/date-range-picker/date-range-picker-two-inputs.spec.ts @@ -1,11 +1,4 @@ -import { - elementUpdated, - expect, - fixture, - html, - waitUntil, -} from '@open-wc/testing'; -import { spy } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import IgcCalendarComponent from '../calendar/calendar.js'; import { CalendarDay } from '../calendar/model.js'; import { @@ -15,8 +8,17 @@ import { escapeKey, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { + elementUpdated, + fixture, + html, + waitUntil, +} from '../common/helpers.spec.js'; import { checkDatesEqual, + eventMatch, + expectCalledWith, + expectNotCalledWith, isFocused, simulateClick, simulateInput, @@ -32,7 +34,7 @@ import { } from './date-range-picker.utils.spec.js'; describe('Date range picker - two inputs', () => { - before(() => defineComponents(IgcDateRangePickerComponent)); + beforeAll(() => defineComponents(IgcDateRangePickerComponent)); let picker: IgcDateRangePickerComponent; let dateTimeInputs: Array; @@ -149,7 +151,7 @@ describe('Date range picker - two inputs', () => { }); it('should modify value only through calendar selection and not input when nonEditable is true', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.nonEditable = true; await elementUpdated(picker); @@ -161,12 +163,12 @@ describe('Date range picker - two inputs', () => { expect(dateTimeInputs[0].value).to.equal(null); expect(picker.value).to.deep.equal({ start: null, end: null }); expect(calendar.values).to.deep.equal([]); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); await picker.show(); await selectDates(today, tomorrow, calendar); checkSelectedRange(picker, { start: today.native, end: tomorrow.native }); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); }); it('should set properties of the inputs correctly', async () => { @@ -230,7 +232,7 @@ describe('Date range picker - two inputs', () => { }); describe('Methods', () => { it('should clear the input on invoking clear()', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.value = { start: today.native, end: tomorrow.native }; await elementUpdated(picker); @@ -239,13 +241,13 @@ describe('Date range picker - two inputs', () => { picker.clear(); await elementUpdated(picker); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.value).to.deep.equal(null); expect(dateTimeInputs[0].value).to.be.null; expect(dateTimeInputs[1].value).to.be.null; }); it('should select a date range on invoking select', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); expect(picker.value).to.deep.equal({ start: null, end: null }); picker.select({ start: today.native, end: tomorrow.native }); @@ -257,18 +259,18 @@ describe('Date range picker - two inputs', () => { }); expect(dateTimeInputs[0].value).to.equal(picker.value?.start); expect(dateTimeInputs[1].value).to.equal(picker.value?.end); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); }); }); describe('Interactions', () => { describe('Selection via the calendar', () => { it('should select a single date in dropdown mode and emit igcChange', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.open = true; await elementUpdated(picker); await selectDates(today, null, calendar); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkSelectedRange(picker, { start: today.native, end: today.native }); const popover = picker.renderRoot.querySelector('igc-popover'); @@ -277,13 +279,13 @@ describe('Date range picker - two inputs', () => { }); it('should select a range of dates in dropdown mode and emit igcChange', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.open = true; await elementUpdated(picker); await selectDates(today, tomorrow, calendar); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkSelectedRange(picker, { start: today.native, end: tomorrow.native, @@ -295,7 +297,7 @@ describe('Date range picker - two inputs', () => { }); it('should swap the selected dates after input if the end is earlier than the start', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); const aMonthAgo = today.add('month', -1); picker.value = null; await elementUpdated(picker); @@ -307,8 +309,8 @@ describe('Date range picker - two inputs', () => { simulateKeyboard(dateTimeInputs[0], arrowUp); await elementUpdated(picker); - expect(eventSpy).calledWith('igcInput'); - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcInput'); + spy.mockClear(); dateTimeInputs[1].focus(); // first arrow sets today, second sets aMonthAgo @@ -320,7 +322,7 @@ describe('Date range picker - two inputs', () => { dateTimeInputs[1].blur(); await elementUpdated(picker); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkSelectedRange(picker, { start: aMonthAgo.native, end: today.native, @@ -328,7 +330,7 @@ describe('Date range picker - two inputs', () => { }); it('should select a range of dates in dialog mode and emit igcChange when done is clicked', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; await elementUpdated(picker); @@ -337,7 +339,7 @@ describe('Date range picker - two inputs', () => { await elementUpdated(picker); await selectDates(today, tomorrow, calendar); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); let dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(true); @@ -346,7 +348,7 @@ describe('Date range picker - two inputs', () => { ) as HTMLButtonElement; doneBtn?.click(); await elementUpdated(picker); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkSelectedRange(picker, { start: today.native, end: tomorrow.native, @@ -357,7 +359,7 @@ describe('Date range picker - two inputs', () => { }); it('should select a range of dates in dialog mode and emit igcChange when clicked outside of dialog', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; await elementUpdated(picker); @@ -366,7 +368,7 @@ describe('Date range picker - two inputs', () => { await elementUpdated(picker); await selectDates(today, tomorrow, calendar); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); let dialog = picker.renderRoot.querySelector( 'igc-dialog' ) as IgcDialogComponent; @@ -377,10 +379,10 @@ describe('Date range picker - two inputs', () => { simulateClick(nativeDialog, { clientX: x + 1, clientY: y - 1 }); await elementUpdated(dialog); - await waitUntil(() => eventSpy.calledWith('igcClosed')); + await waitUntil(() => eventMatch(spy, 'igcClosed')); await elementUpdated(picker); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkSelectedRange(picker, { start: today.native, end: tomorrow.native, @@ -393,7 +395,7 @@ describe('Date range picker - two inputs', () => { }); it('should not emit igcChange when cancel is clicked and the value should be the initial value', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; const date1 = new CalendarDay({ @@ -413,7 +415,7 @@ describe('Date range picker - two inputs', () => { await elementUpdated(picker); await selectDates(date1, date2, calendar); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); let dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(true); checkSelectedRange(picker, { start: date1.native, end: date2.native }); @@ -423,7 +425,7 @@ describe('Date range picker - two inputs', () => { ) as HTMLButtonElement; cancelBtn?.click(); await elementUpdated(picker); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(false); checkSelectedRange(picker, { @@ -433,7 +435,7 @@ describe('Date range picker - two inputs', () => { }); it('should revert to no value when cancel is clicked and initial value is null', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; const date1 = new CalendarDay({ @@ -453,7 +455,7 @@ describe('Date range picker - two inputs', () => { await elementUpdated(picker); await selectDates(date1, date2, calendar); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); let dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(true); checkSelectedRange(picker, { start: date1.native, end: date2.native }); @@ -463,7 +465,7 @@ describe('Date range picker - two inputs', () => { ) as HTMLButtonElement; cancelBtn?.click(); await elementUpdated(picker); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(false); @@ -480,7 +482,7 @@ describe('Date range picker - two inputs', () => { }); it('should not emit igcChange when escape is pressed and the value should be the initial value', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; const date1 = new CalendarDay({ @@ -500,7 +502,7 @@ describe('Date range picker - two inputs', () => { await elementUpdated(picker); await selectDates(date1, date2, calendar); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); let dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(true); checkSelectedRange(picker, { start: date1.native, end: date2.native }); @@ -509,7 +511,7 @@ describe('Date range picker - two inputs', () => { await elementUpdated(picker); await elementUpdated(dateTimeInputs[0]); await elementUpdated(dateTimeInputs[1]); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); dialog = picker.renderRoot.querySelector('igc-dialog'); expect(dialog?.hasAttribute('open')).to.equal(false); @@ -523,14 +525,14 @@ describe('Date range picker - two inputs', () => { describe('Interactions with the inputs and the open and clear buttons', () => { it('should emit or not igcInput according to nonEditable property', async () => { const expectedValue = today.native; - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); dateTimeInputs[0].focus(); simulateKeyboard(dateTimeInputs[0], arrowUp); await elementUpdated(picker); - expect(eventSpy).calledOnceWith('igcInput'); - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcInput'); + spy.mockClear(); expect(dateTimeInputs[0].value).to.not.be.null; checkDatesEqual(dateTimeInputs[0].value!, expectedValue); @@ -542,7 +544,7 @@ describe('Date range picker - two inputs', () => { simulateKeyboard(dateTimeInputs[0], arrowUp); await elementUpdated(picker); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(dateTimeInputs[0].value).to.be.null; dateTimeInputs[0].dispatchEvent( @@ -550,7 +552,7 @@ describe('Date range picker - two inputs', () => { ); await elementUpdated(picker); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.value).to.deep.equal(null); }); @@ -579,7 +581,7 @@ describe('Date range picker - two inputs', () => { }); it('should clear the inputs on clicking the clear icon', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.value = { start: today.native, end: tomorrow.native }; await elementUpdated(picker); @@ -592,7 +594,7 @@ describe('Date range picker - two inputs', () => { await elementUpdated(dateTimeInputs[0]); expect(isFocused(dateTimeInputs[0])).to.be.false; - expect(eventSpy).to.be.calledWith('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: { start: null, end: null }, }); expect(picker.open).to.be.false; @@ -602,7 +604,7 @@ describe('Date range picker - two inputs', () => { }); it('should emit igcInput and igcChange on input value change', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); dateTimeInputs[0].focus(); await elementUpdated(dateTimeInputs[0]); @@ -611,28 +613,28 @@ describe('Date range picker - two inputs', () => { simulateKeyboard(dateTimeInputs[0], arrowUp); await elementUpdated(picker); - expect(eventSpy).calledWith('igcInput', { + expect(spy).toHaveBeenCalledWith('igcInput', { detail: { start: today.native, end: null }, }); - eventSpy.resetHistory(); + spy.mockClear(); dateTimeInputs[0].setSelectionRange(0, 1); // make sure caret is on the month part (MM/dd/yyyy) simulateKeyboard(dateTimeInputs[0], arrowDown); await elementUpdated(picker); - expect(eventSpy).calledWith('igcInput', { + expect(spy).toHaveBeenCalledWith('igcInput', { detail: { start: today.add('month', -1).native, end: null }, }); - eventSpy.resetHistory(); + spy.mockClear(); dateTimeInputs[0].blur(); - expect(eventSpy).calledWith('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: { start: today.add('month', -1).native, end: null }, }); }); it('should set the calendar active date to the altered date of the range while typing', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); const aMonthAgo = today.add('month', -1); picker.value = null; picker.open = true; @@ -645,9 +647,9 @@ describe('Date range picker - two inputs', () => { simulateKeyboard(dateTimeInputs[0], arrowDown); await elementUpdated(picker); - expect(eventSpy).calledWith('igcInput'); - expect(eventSpy).not.calledWith('igcChange'); - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcInput'); + expectNotCalledWith(spy, 'igcChange'); + spy.mockClear(); checkDatesEqual(dateTimeInputs[0].value!, today.native); // typing a single date does not select a range in the calendar checkSelectedRange(picker, { start: today.native, end: null }); @@ -657,17 +659,17 @@ describe('Date range picker - two inputs', () => { expect(isFocused(dateTimeInputs[1])).to.be.true; // emit igcChange on losing focus of the edited input - expect(eventSpy).calledWith('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: { start: today.native, end: null }, }); - eventSpy.resetHistory(); + spy.mockClear(); dateTimeInputs[1].setSelectionRange(0, 1); simulateKeyboard(dateTimeInputs[1], arrowDown); await elementUpdated(picker); - expect(eventSpy).calledWith('igcInput'); - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcInput'); + spy.mockClear(); checkDatesEqual(dateTimeInputs[1].value!, today.native); // typing the end date as well results in a selected range of a single date checkSelectedRange(picker, { start: today.native, end: today.native }); @@ -676,8 +678,8 @@ describe('Date range picker - two inputs', () => { simulateKeyboard(dateTimeInputs[1], arrowDown); await elementUpdated(picker); - expect(eventSpy).calledWith('igcInput'); - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcInput'); + spy.mockClear(); checkDatesEqual(dateTimeInputs[1].value!, aMonthAgo.native); // changing the end date while typing alters the selected range // the active date is set to the typed date, in this case the end one @@ -692,13 +694,13 @@ describe('Date range picker - two inputs', () => { expect(isFocused(dateTimeInputs[0])).to.be.true; await elementUpdated(picker); - expect(eventSpy).calledWith('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: { start: aMonthAgo.native, end: today.native }, }); }); it('should set the calendar active date to the typed date and reflect selection in calendar', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.value = null; picker.open = true; await elementUpdated(picker); @@ -719,7 +721,7 @@ describe('Date range picker - two inputs', () => { checkSelectedRange(picker, { start: expectedDate, end: null }); checkDatesEqual(calendar.activeDate, expectedDate); - expect(eventSpy).calledWith('igcInput', { + expect(spy).toHaveBeenCalledWith('igcInput', { detail: { start: expectedDate, end: null }, }); @@ -739,13 +741,13 @@ describe('Date range picker - two inputs', () => { checkSelectedRange(picker, { start: expectedDate, end: expectedDate2 }); checkDatesEqual(calendar.activeDate, expectedDate2); - expect(eventSpy).calledWith('igcInput', { + expect(spy).toHaveBeenCalledWith('igcInput', { detail: { start: expectedDate, end: expectedDate2 }, }); }); it('should set the calendar active date to any of the defined dates start/end', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); const march1st2025 = today.set({ month: 2, date: 1, @@ -768,7 +770,7 @@ describe('Date range picker - two inputs', () => { simulateInput(input1, { inputType: 'deleteContentBackward' }); await elementUpdated(picker); - expect(eventSpy).calledWith('igcInput'); + expectCalledWith(spy, 'igcInput'); // the active date is set to the end date as it is the first defined in the range checkDatesEqual(calendar.activeDate, june3rd2025); @@ -781,7 +783,7 @@ describe('Date range picker - two inputs', () => { checkDatesEqual(calendar.activeDate, june3rd2025); }); it('should toggle the calendar with keyboard combinations and keep focus', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); dateTimeInputs[0].focus(); expect(isFocused(dateTimeInputs[0])).to.be.true; @@ -791,21 +793,25 @@ describe('Date range picker - two inputs', () => { expect(picker.open).to.be.true; expect(isFocused(dateTimeInputs[0])).to.be.false; - expect(eventSpy.firstCall).calledWith('igcOpening'); - expect(eventSpy.lastCall).calledWith('igcOpened'); - eventSpy.resetHistory(); + expect(spy).toHaveBeenNthCalledWith(1, 'igcOpening', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcOpened'); + spy.mockClear(); simulateKeyboard(dateTimeInputs[0], [altKey, arrowUp]); await elementUpdated(picker); expect(picker.open).to.be.false; expect(isFocused(dateTimeInputs[0])).to.be.true; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.lastCall).calledWith('igcClosed'); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosed'); simulateKeyboard(dateTimeInputs[0], [altKey, arrowDown]); await elementUpdated(picker); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(dateTimeInputs[0], escapeKey); await elementUpdated(picker); @@ -813,8 +819,10 @@ describe('Date range picker - two inputs', () => { expect(picker.open).to.be.false; expect(isFocused(dateTimeInputs[0])).to.be.true; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.lastCall).calledWith('igcClosed'); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosed'); }); }); describe('Readonly state', () => { @@ -823,7 +831,7 @@ describe('Date range picker - two inputs', () => { await elementUpdated(picker); }); it('should not modify value through selection or typing when readOnly is true', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); expect(picker.value).to.deep.equal({ start: null, end: null }); await picker.show(); @@ -831,7 +839,7 @@ describe('Date range picker - two inputs', () => { expect(picker.value).to.deep.equal({ start: null, end: null }); expect(calendar.values).to.deep.equal([]); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); await picker.hide(); @@ -842,10 +850,10 @@ describe('Date range picker - two inputs', () => { expect(dateTimeInputs[0].value).to.equal(null); expect(picker.value).to.deep.equal({ start: null, end: null }); expect(calendar.values).to.deep.equal([]); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); }); it('should not clear the inputs via the clear icon when readOnly is true', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); const testValue = { start: today.native, end: tomorrow.native }; picker.value = testValue; await elementUpdated(picker); @@ -853,21 +861,21 @@ describe('Date range picker - two inputs', () => { simulateClick(getIcon(picker, clearIcon)); expect(picker.value).to.deep.equal(testValue); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); checkSelectedRange(picker, testValue); }); it('should not open the calendar on clicking the input - dropdown mode', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); const nativeInput = dateTimeInputs[0].renderRoot.querySelector('input')!; simulateClick(nativeInput); await elementUpdated(picker); expect(picker.open).to.be.false; - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); }); it('should not open the calendar on clicking the input - dialog mode', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.mode = 'dialog'; await elementUpdated(picker); @@ -877,7 +885,7 @@ describe('Date range picker - two inputs', () => { await elementUpdated(picker); expect(picker.open).to.be.false; - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); }); }); }); diff --git a/src/components/date-range-picker/date-range-picker.common.spec.ts b/src/components/date-range-picker/date-range-picker.common.spec.ts index 0a917f607..e4799ad5c 100644 --- a/src/components/date-range-picker/date-range-picker.common.spec.ts +++ b/src/components/date-range-picker/date-range-picker.common.spec.ts @@ -1,6 +1,13 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import type Sinon from 'sinon'; -import { spy } from 'sinon'; +import { + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + type MockInstance, + vi, +} from 'vitest'; import type IgcButtonComponent from '../button/button.js'; import IgcCalendarComponent from '../calendar/calendar.js'; import { CalendarDay } from '../calendar/model.js'; @@ -13,9 +20,13 @@ import { escapeKey, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import type { IgcDateRangePickerResourceStrings } from '../common/i18n/EN/date-range-picker.resources.js'; +import { first, last } from '../common/util.js'; import { checkDatesEqual, + expectCalledWith, + expectNotCalledWith, simulateClick, simulateKeyboard, } from '../common/utils.spec.js'; @@ -33,7 +44,7 @@ import { import IgcPredefinedRangesAreaComponent from './predefined-ranges-area.js'; describe('Date range picker - common tests for single and two inputs mode', () => { - before(() => defineComponents(IgcDateRangePickerComponent)); + beforeAll(() => defineComponents(IgcDateRangePickerComponent)); let picker: IgcDateRangePickerComponent; let calendar: IgcCalendarComponent; @@ -225,12 +236,12 @@ describe('Date range picker - common tests for single and two inputs mode', () = }); it('should keep the picker open when keepOpenOnSelect is enabled and a selection is made in the calendar picker', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.keepOpenOnSelect = true; await elementUpdated(picker); await picker.show(); await selectDates(today, tomorrow, calendar); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); checkSelectedRange( picker, { start: today.native, end: tomorrow.native }, @@ -307,7 +318,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = it('should default inputFormat to whatever Intl.DateTimeFormat returns for the current locale', async () => { const defaultFormat = 'MM/dd/yyyy'; - expect(picker.locale).to.equal('en-US'); + expect(picker.locale).to.equal('en'); expect(picker.inputFormat).to.equal(defaultFormat); picker.locale = 'fr'; @@ -317,7 +328,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = }); it('should use the value of locale format for displayFormat, if it is not defined', async () => { - expect(picker.locale).to.equal('en-US'); + expect(picker.locale).to.equal('en'); expect(picker.getAttribute('display-format')).to.be.null; expect(picker.displayFormat).to.equal('M/d/yyyy'); @@ -375,6 +386,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = for (const test of tests) { picker.resourceStrings[test.key] = test.value; } + picker.resourceStrings = { ...picker.resourceStrings }; picker.usePredefinedRanges = true; await elementUpdated(picker); @@ -440,34 +452,34 @@ describe('Date range picker - common tests for single and two inputs mode', () = describe('Methods', () => { it('should open/close the picker on invoking show/hide/toggle and not emit events', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); expect(picker.open).to.be.false; await picker.show(); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.open).to.be.true; await picker.hide(); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.open).to.be.false; await picker.toggle(); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.open).to.be.true; await picker.toggle(); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(picker.open).to.be.false; }); }); describe('Interactions', () => { describe('Selection via the calendar', () => { it('should not emit igcChange when value is unchanged and done is clicked (dialog)', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.value = { start: today.native, end: tomorrow.native }; picker.mode = 'dialog'; await elementUpdated(picker); @@ -475,7 +487,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = picker.open = true; await elementUpdated(picker); - expect(eventSpy).not.to.be.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); const dialog = picker.renderRoot.querySelector( IgcDialogComponent.tagName ); @@ -486,25 +498,25 @@ describe('Date range picker - common tests for single and two inputs mode', () = ) as HTMLButtonElement; doneBtn?.click(); await elementUpdated(picker); - expect(eventSpy).not.calledWith('igcChange'); + expectNotCalledWith(spy, 'igcChange'); }); }); describe('Keyboard navigation', () => { it('should close the picker when in open state on pressing Escape', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.focus(); simulateKeyboard(picker, escapeKey); await elementUpdated(picker); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); await picker.show(); simulateKeyboard(picker, escapeKey); await elementUpdated(picker); - expect(eventSpy).calledTwice; - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).calledWith('igcClosed'); - eventSpy.resetHistory(); + expect(spy.mock.calls).lengthOf(2); + expectCalledWith(spy, 'igcClosing'); + expectCalledWith(spy, 'igcClosed'); + spy.mockClear(); // dialog mode picker.mode = 'dialog'; @@ -515,35 +527,34 @@ describe('Date range picker - common tests for single and two inputs mode', () = simulateKeyboard(picker, escapeKey); await elementUpdated(picker); - expect(eventSpy).calledTwice; - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).calledWith('igcClosed'); + expect(spy.mock.calls).lengthOf(2); + expectCalledWith(spy, 'igcClosing'); + expectCalledWith(spy, 'igcClosed'); }); it('should open the calendar picker on Alt + ArrowDown and close it on Alt + ArrowUp - dropdown mode', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); expect(picker.open).to.be.false; picker.focus(); simulateKeyboard(picker, [altKey, arrowDown]); await elementUpdated(picker); expect(picker.open).to.be.true; - expect(eventSpy).calledWith('igcOpening'); - expect(eventSpy).calledWith('igcOpened'); + expectCalledWith(spy, 'igcOpening'); + expectCalledWith(spy, 'igcOpened'); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(picker, [altKey, arrowUp]); await elementUpdated(picker); expect(picker.open).to.be.false; - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).calledWith('igcClosed'); - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcClosing'); + expectCalledWith(spy, 'igcClosed'); }); it('should open the calendar picker on Alt + ArrowDown and close it on Alt + ArrowUp - dialog mode', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); expect(picker.open).to.be.false; picker.focus(); picker.mode = 'dialog'; @@ -557,16 +568,16 @@ describe('Date range picker - common tests for single and two inputs mode', () = ); expect(picker.open).to.be.true; expect(dialog?.open).to.be.true; - expect(eventSpy).calledWith('igcOpening'); - expect(eventSpy).calledWith('igcOpened'); - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcOpening'); + expectCalledWith(spy, 'igcOpened'); + spy.mockClear(); simulateKeyboard(picker, [altKey, arrowUp]); await elementUpdated(picker); expect(picker.open).to.be.false; - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).calledWith('igcClosed'); + expectCalledWith(spy, 'igcClosing'); + expectCalledWith(spy, 'igcClosed'); }); describe('Interactions with the show icon', () => { @@ -589,7 +600,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = }); describe('Readonly state', () => { it('should not modify value through selection when readOnly is true', async () => { - const eventSpy = spy(picker, 'emitEvent'); + const spy = vi.spyOn(picker, 'emitEvent'); picker.readOnly = true; await elementUpdated(picker); expect(picker.value).to.deep.equal({ start: null, end: null }); @@ -602,7 +613,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = await selectDates(today, tomorrow, calendar); expect(picker.value).to.deep.equal({ start: null, end: null }); expect(calendar.values).to.deep.equal([]); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); }); describe('Dropdown mode', () => { beforeEach(async () => { @@ -672,7 +683,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = }); }); describe('Predefined ranges', () => { - let eventSpy: Sinon.SinonSpy; + let spy: MockInstance; const previousThreeDaysStart = CalendarDay.today.add('day', -3).native; const nextThreeDaysEnd = CalendarDay.today.add('day', 3).native; @@ -694,15 +705,15 @@ describe('Date range picker - common tests for single and two inputs mode', () = ]; beforeEach(async () => { - eventSpy = spy(picker, 'emitEvent'); + spy = vi.spyOn(picker, 'emitEvent'); }); afterEach(() => { - eventSpy.resetHistory(); + spy.mockClear(); }); function getEventArgs() { - return eventSpy.firstCall.lastArg.detail as DateRangeValue; + return last(first(spy.mock.calls)).detail as DateRangeValue; } function getPredefinedArea() { @@ -748,7 +759,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = simulateClick(chip); await elementUpdated(chip); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); const range = getEventArgs(); expect(picker.activeDate).to.eql(range.start); @@ -758,7 +769,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = false ); expect(popover?.open).to.be.false; - eventSpy.resetHistory(); + spy.mockClear(); } }); @@ -772,14 +783,17 @@ describe('Date range picker - common tests for single and two inputs mode', () = picker.open = true; await elementUpdated(picker); - const chipSpy = spy(getPredefinedArea()!, '_handleRangeSelect' as any); + const chipSpy = vi.spyOn( + getPredefinedArea()!, + '_handleRangeSelect' as any + ); for (const chip of getRangeChips()) { await picker.show(); simulateClick(chip); await elementUpdated(picker); - const range = chipSpy.firstCall.firstArg as DateRangeValue; + const range = chipSpy.mock.calls[0][0] as DateRangeValue; expect(picker.activeDate).to.eql(range.start); checkSelectedRange( @@ -795,15 +809,15 @@ describe('Date range picker - common tests for single and two inputs mode', () = simulateClick(getDialogDoneButton()); await elementUpdated(picker); - if (eventSpy.args.length > 2) { - expect(eventSpy).calledWith('igcChange'); + if (spy.mock.calls.length > 2) { + expectCalledWith(spy, 'igcChange'); } - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).calledWith('igcClosed'); + expectCalledWith(spy, 'igcClosing'); + expectCalledWith(spy, 'igcClosed'); expect(getDialog()?.open).to.be.false; - chipSpy.resetHistory(); - eventSpy.resetHistory(); + chipSpy.mockClear(); + spy.mockClear(); } }); @@ -819,7 +833,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = simulateClick(chip); await elementUpdated(picker); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); const range = getEventArgs(); expect(picker.activeDate).to.eql(range.start); @@ -832,7 +846,7 @@ describe('Date range picker - common tests for single and two inputs mode', () = false ); expect(popover?.open).to.be.false; - eventSpy.resetHistory(); + spy.mockClear(); } }); }); diff --git a/src/components/date-range-picker/date-range-picker.utils.spec.ts b/src/components/date-range-picker/date-range-picker.utils.spec.ts index 7acf36532..457ddd2b1 100644 --- a/src/components/date-range-picker/date-range-picker.utils.spec.ts +++ b/src/components/date-range-picker/date-range-picker.utils.spec.ts @@ -1,7 +1,8 @@ -import { elementUpdated, expect } from '@open-wc/testing'; +import { expect } from 'vitest'; import IgcCalendarComponent from '../calendar/calendar.js'; import { getCalendarDOM, getDOMDate } from '../calendar/helpers.spec.js'; import type { CalendarDay } from '../calendar/model.js'; +import { elementUpdated } from '../common/helpers.spec.js'; import { equal } from '../common/util.js'; import { checkDatesEqual, simulateClick } from '../common/utils.spec.js'; import IgcDateTimeInputComponent from '../date-time-input/date-time-input.js'; diff --git a/src/components/date-range-picker/predefined-ranges-area.spec.ts b/src/components/date-range-picker/predefined-ranges-area.spec.ts index e97a00109..9bf9da0e4 100644 --- a/src/components/date-range-picker/predefined-ranges-area.spec.ts +++ b/src/components/date-range-picker/predefined-ranges-area.spec.ts @@ -1,15 +1,15 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; -import { spy } from 'sinon'; import { CalendarDay } from '../calendar/model.js'; import IgcChipComponent from '../chip/chip.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { simulateClick } from '../common/utils.spec.js'; import type { CustomDateRange } from './date-range-picker.js'; import IgcPredefinedRangesAreaComponent from './predefined-ranges-area.js'; describe('Predefined Area', () => { - before(() => { + beforeAll(() => { defineComponents(IgcPredefinedRangesAreaComponent); }); @@ -121,7 +121,7 @@ describe('Predefined Area', () => { createComponentWithCustomRanges() ); - const eventSpy = spy(); + const eventSpy = vi.fn(); component.addEventListener('igcRangeSelect', eventSpy); const chips = getChips(); @@ -134,8 +134,9 @@ describe('Predefined Area', () => { simulateClick(chip); await elementUpdated(component); - expect(eventSpy.calledWithMatch({ detail: ranges[idx].dateRange })).to - .be.true; + expect(eventSpy).toHaveBeenCalledWith( + expect.objectContaining({ detail: ranges[idx].dateRange }) + ); } }); }); diff --git a/src/components/date-time-input/date-time-input.spec.ts b/src/components/date-time-input/date-time-input.spec.ts index 824ec677a..851fcdf2b 100644 --- a/src/components/date-time-input/date-time-input.spec.ts +++ b/src/components/date-time-input/date-time-input.spec.ts @@ -1,5 +1,4 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import { spy } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { CalendarDay, toCalendarDay } from '../calendar/model.js'; import { altKey, @@ -10,8 +9,10 @@ import { ctrlKey, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, + expectCalledWith, isFocused, simulateInput, simulateKeyboard, @@ -27,7 +28,7 @@ import IgcDateTimeInputComponent from './date-time-input.js'; import { DatePart, type DatePartDeltas, DateTimeUtil } from './date-util.js'; describe('Date Time Input component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcDateTimeInputComponent); }); @@ -188,7 +189,7 @@ describe('Date Time Input component', () => { }); it('should emit igcChange on blur after an incomplete mask has been parsed - issue #1695', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); el.focus(); await elementUpdated(el); @@ -201,7 +202,7 @@ describe('Date Time Input component', () => { el.blur(); await elementUpdated(el); - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); expect(input.value).to.deep.equal('1/1/2000'); }); @@ -568,17 +569,17 @@ describe('Date Time Input component', () => { el.focus(); await elementUpdated(el); - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); simulateKeyboard(input, [altKey, arrowUp]); await elementUpdated(el); - expect(eventSpy).not.to.have.been.called; + expect(spy).not.toHaveBeenCalled(); simulateKeyboard(input, [altKey, arrowDown]); await elementUpdated(el); - expect(eventSpy).not.to.have.been.called; + expect(spy).not.toHaveBeenCalled(); }); it('Alt + ArrowUp/Down is a no-op', async () => { @@ -587,22 +588,21 @@ describe('Date Time Input component', () => { el.focus(); await elementUpdated(el); - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); simulateKeyboard(input, [altKey, arrowUp]); await elementUpdated(el); - expect(eventSpy).not.to.have.been.called; + expect(spy).not.toHaveBeenCalled(); simulateKeyboard(input, [altKey, arrowDown]); await elementUpdated(el); - expect(eventSpy).not.to.have.been.called; + expect(spy).not.toHaveBeenCalled(); }); it('should not emit change event when readonly', async () => { - const eventSpy = spy(el, 'emitEvent'); - + const spy = vi.spyOn(el, 'emitEvent'); el.value = new Date(2023, 5, 1); el.readOnly = true; el.focus(); @@ -611,7 +611,7 @@ describe('Date Time Input component', () => { el.blur(); await elementUpdated(el); - expect(eventSpy.getCalls()).empty; + expect(spy).not.toHaveBeenCalled(); }); it('should not move input selection (caret) from a focused part when stepUp/stepDown are invoked', async () => { @@ -890,7 +890,7 @@ describe('Date Time Input component', () => { }); it('should emit events correctly', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); el.focus(); await elementUpdated(el); @@ -899,24 +899,24 @@ describe('Date Time Input component', () => { simulateKeyboard(input, arrowUp); await elementUpdated(el); - expect(eventSpy).calledWith('igcInput'); - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcInput'); + spy.mockClear(); simulateKeyboard(input, arrowDown); await elementUpdated(el); - expect(eventSpy).calledWith('igcInput'); - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcInput'); + spy.mockClear(); simulateWheel(input, { deltaY: -125 }); await elementUpdated(el); - expect(eventSpy).calledWith('igcInput'); - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcInput'); + spy.mockClear(); el.blur(); await elementUpdated(el); expect(isFocused(el)).to.be.false; - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); el.clear(); await elementUpdated(el); @@ -930,7 +930,7 @@ describe('Date Time Input component', () => { el.blur(); await elementUpdated(el); expect(isFocused(el)).to.be.false; - expect(eventSpy).calledWith('igcChange'); + expectCalledWith(spy, 'igcChange'); }); }); diff --git a/src/components/date-time-input/date-util.spec.ts b/src/components/date-time-input/date-util.spec.ts index c630e4b5b..6e2dfd414 100644 --- a/src/components/date-time-input/date-util.spec.ts +++ b/src/components/date-time-input/date-util.spec.ts @@ -1,5 +1,4 @@ -import { expect } from '@open-wc/testing'; - +import { describe, expect, it } from 'vitest'; import { DateParts, DateTimeUtil } from './date-util.js'; describe('Date Util', () => { diff --git a/src/components/dialog/dialog.spec.ts b/src/components/dialog/dialog.spec.ts index 6403ccbc1..f707bfe20 100644 --- a/src/components/dialog/dialog.spec.ts +++ b/src/components/dialog/dialog.spec.ts @@ -1,19 +1,22 @@ +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import IgcButtonComponent from '../button/button.js'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, waitUntil, -} from '@open-wc/testing'; -import { spy } from 'sinon'; - -import IgcButtonComponent from '../button/button.js'; -import { defineComponents } from '../common/definitions/defineComponents.js'; -import { simulateClick } from '../common/utils.spec.js'; +} from '../common/helpers.spec.js'; +import { + eventMatch, + expectCalledWith, + expectNotCalledWith, + simulateClick, +} from '../common/utils.spec.js'; import IgcDialogComponent from './dialog.js'; describe('Dialog', () => { - before(() => { + beforeAll(() => { defineComponents(IgcDialogComponent); }); @@ -159,21 +162,21 @@ describe('Dialog', () => { }); it('does not emit events through API calls', async () => { - const eventSpy = spy(dialog, 'emitEvent'); + const spy = vi.spyOn(dialog, 'emitEvent'); await dialog.show(); expect(dialog.open).to.be.true; - expect(eventSpy.getCalls()).is.empty; + expect(spy.mock.calls).to.be.empty; await dialog.hide(); expect(dialog.open).to.be.false; - expect(eventSpy.getCalls()).is.empty; + expect(spy.mock.calls).to.be.empty; dialog.open = false; await elementUpdated(dialog); expect(dialog.open).to.be.false; - expect(eventSpy.getCalls()).is.empty; + expect(spy.mock.calls).to.be.empty; }); }); @@ -186,20 +189,22 @@ describe('Dialog', () => { }); it('should close the dialog when the user presses Escape', async () => { - const eventSpy = spy(dialog, 'emitEvent'); + const spy = vi.spyOn(dialog, 'emitEvent'); await dialog.show(); nativeDialog.dispatchEvent(new Event('cancel')); await elementUpdated(dialog); await waitUntil(() => !dialog.open); - expect(eventSpy.getCalls()).lengthOf(2); - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.secondCall).calledWith('igcClosed'); + expect(spy.mock.calls).lengthOf(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosed'); }); it('should not close the dialog when the user presses Escape and `keepOpenOnEscape` is set', async () => { - const eventSpy = spy(dialog, 'emitEvent'); + const spy = vi.spyOn(dialog, 'emitEvent'); dialog.keepOpenOnEscape = true; await dialog.show(); @@ -208,11 +213,11 @@ describe('Dialog', () => { await elementUpdated(dialog); expect(dialog.open).to.be.true; - expect(eventSpy.getCalls()).is.empty; + expect(spy.mock.calls).to.be.empty; }); it('default action button emits closing events', async () => { - const eventSpy = spy(dialog, 'emitEvent'); + const spy = vi.spyOn(dialog, 'emitEvent'); await dialog.show(); simulateClick( @@ -221,16 +226,18 @@ describe('Dialog', () => { await elementUpdated(dialog); await waitUntil(() => !dialog.open); - expect(eventSpy.getCalls()).lengthOf(2); - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.secondCall).calledWith('igcClosed'); + expect(spy.mock.calls).lengthOf(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosed'); }); it('can cancel `igcClosing` events when clicking outside the dialog area', async () => { dialog.closeOnOutsideClick = true; await dialog.show(); - const eventSpy = spy(dialog, 'emitEvent'); + const spy = vi.spyOn(dialog, 'emitEvent'); dialog.addEventListener('igcClosing', (e) => e.preventDefault(), { once: true, }); @@ -239,8 +246,8 @@ describe('Dialog', () => { simulateClick(nativeDialog, { clientX: x - 1, clientY: y - 1 }); await elementUpdated(dialog); - expect(eventSpy).calledWith('igcClosing'); - expect(eventSpy).not.calledWith('igcClosed'); + expectCalledWith(spy, 'igcClosing'); + expectNotCalledWith(spy, 'igcClosed'); }); it('does not close the dialog on clicking outside when `closeOnOutsideClick` is not set', async () => { @@ -257,13 +264,13 @@ describe('Dialog', () => { dialog.closeOnOutsideClick = true; await dialog.show(); - const eventSpy = spy(dialog, 'emitEvent'); + const spy = vi.spyOn(dialog, 'emitEvent'); const { x, y } = dialog.getBoundingClientRect(); simulateClick(nativeDialog, { clientX: x + 1, clientY: y - 1 }); await elementUpdated(dialog); - await waitUntil(() => eventSpy.calledWith('igcClosed')); + await waitUntil(() => eventMatch(spy, 'igcClosed')); expect(dialog.open).to.be.false; }); diff --git a/src/components/divider/divider.spec.ts b/src/components/divider/divider.spec.ts index 5bd99dcb5..e648b07c4 100644 --- a/src/components/divider/divider.spec.ts +++ b/src/components/divider/divider.spec.ts @@ -1,14 +1,14 @@ -import { expect, fixture, html } from '@open-wc/testing'; - +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { fixture, html } from '../common/helpers.spec.js'; import IgcDividerComponent from './divider.js'; describe('Divider', () => { - before(() => { + beforeAll(() => { defineComponents(IgcDividerComponent); }); - const createDefaultDivider = () => html` `; + const createDefaultDivider = () => html``; const createVerticalDashedDivider = () => html` diff --git a/src/components/dropdown/dropdown.spec.ts b/src/components/dropdown/dropdown.spec.ts index fb7cbafec..2986be725 100644 --- a/src/components/dropdown/dropdown.spec.ts +++ b/src/components/dropdown/dropdown.spec.ts @@ -1,6 +1,4 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import { spy } from 'sinon'; - +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import IgcButtonComponent from '../button/button.js'; import { arrowDown, @@ -12,6 +10,7 @@ import { tabKey, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { simulateClick, simulateKeyboard, @@ -28,7 +27,7 @@ type ItemState = { }; describe('Dropdown', () => { - before(() => defineComponents(IgcDropdownComponent, IgcButtonComponent)); + beforeAll(() => defineComponents(IgcDropdownComponent, IgcButtonComponent)); let dropDown: IgcDropdownComponent; const Items = [ @@ -144,15 +143,18 @@ describe('Dropdown', () => { }); it('`close` behavior', async () => { - const eventSpy = spy(dropDown, 'emitEvent'); + const spy = vi.spyOn(dropDown, 'emitEvent'); dropDown.scrollStrategy = 'close'; await openDropdown(); await simulateScroll(container, { top: 200 }); expect(dropDown.open).to.be.false; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.lastCall).calledWith('igcClosed'); + expect(spy.mock.calls).lengthOf(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosed'); }); it('`block behavior`', async () => { @@ -205,7 +207,7 @@ describe('Dropdown', () => { }); it('relevant events are fired in order', async () => { - const eventSpy = spy(dropDown, 'emitEvent'); + const spy = vi.spyOn(dropDown, 'emitEvent'); // No opening sequence of events since detached dropdowns are opened with API invocation @@ -217,11 +219,13 @@ describe('Dropdown', () => { simulateKeyboard(btn, enterKey); await elementUpdated(dropDown); - expect(eventSpy.firstCall).calledWith('igcChange', { + expect(spy).toHaveBeenNthCalledWith(1, 'igcChange', { detail: dropDown.selectedItem, }); - expect(eventSpy.secondCall).calledWith('igcClosing'); - expect(eventSpy.thirdCall).calledWith('igcClosed'); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(3, 'igcClosed'); }); it('outside click behavior is enforced', async () => { @@ -676,32 +680,34 @@ describe('Dropdown', () => { }); it('does not emit events on API calls', async () => { - const eventSpy = spy(dropDown, 'emitEvent'); + const spy = vi.spyOn(dropDown, 'emitEvent'); await openDropdown(); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); await closeDropdown(); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); dropDown.select('Testing'); await elementUpdated(dropDown); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); }); it('emits correct order of events on opening', async () => { - const eventSpy = spy(dropDown, 'emitEvent'); + const spy = vi.spyOn(dropDown, 'emitEvent'); simulateClick(getTarget()); await elementUpdated(dropDown); expect(dropDown.open).to.be.true; - expect(eventSpy.firstCall).calledWith('igcOpening'); - expect(eventSpy.secondCall).calledWith('igcOpened'); + expect(spy).toHaveBeenNthCalledWith(1, 'igcOpening', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcOpened'); }); it('emits correct order of events on closing', async () => { - const eventSpy = spy(dropDown, 'emitEvent'); + const spy = vi.spyOn(dropDown, 'emitEvent'); await openDropdown(); @@ -709,12 +715,14 @@ describe('Dropdown', () => { await elementUpdated(dropDown); expect(dropDown.open).to.be.false; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.secondCall).calledWith('igcClosed'); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosed'); }); it('emits correct order of events on selection', async () => { - const eventSpy = spy(dropDown, 'emitEvent'); + const spy = vi.spyOn(dropDown, 'emitEvent'); let targetItem = dropDown.items[3]; // Selection through click @@ -724,13 +732,15 @@ describe('Dropdown', () => { await elementUpdated(dropDown); expect(dropDown.open).to.be.false; - expect(eventSpy.firstCall).calledWithExactly('igcChange', { + expect(spy).toHaveBeenNthCalledWith(1, 'igcChange', { detail: targetItem, }); - expect(eventSpy.secondCall).calledWith('igcClosing'); - expect(eventSpy.thirdCall).calledWith('igcClosed'); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(3, 'igcClosed'); - eventSpy.resetHistory(); + spy.mockClear(); // Selection through keyboard targetItem = dropDown.items[2]; @@ -742,15 +752,17 @@ describe('Dropdown', () => { await elementUpdated(dropDown); expect(dropDown.open).to.be.false; - expect(eventSpy.firstCall).calledWithExactly('igcChange', { + expect(spy).toHaveBeenNthCalledWith(1, 'igcChange', { detail: targetItem, }); - expect(eventSpy.secondCall).calledWith('igcClosing'); - expect(eventSpy.thirdCall).calledWith('igcClosed'); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(3, 'igcClosed'); }); it('can halt opening event sequence', async () => { - const eventSpy = spy(dropDown, 'emitEvent'); + const spy = vi.spyOn(dropDown, 'emitEvent'); dropDown.addEventListener('igcOpening', (e) => e.preventDefault(), { once: true, }); @@ -759,12 +771,14 @@ describe('Dropdown', () => { await elementUpdated(dropDown); expect(dropDown.open).to.be.false; - expect(eventSpy.firstCall).calledWith('igcOpening'); - expect(eventSpy.secondCall).to.be.null; + expect(spy.mock.calls).lengthOf(1); + expect(spy).toHaveBeenNthCalledWith(1, 'igcOpening', { + cancelable: true, + }); }); it('can halt closing event sequence', async () => { - const eventSpy = spy(dropDown, 'emitEvent'); + const spy = vi.spyOn(dropDown, 'emitEvent'); dropDown.addEventListener('igcClosing', (e) => e.preventDefault(), { once: true, }); @@ -776,10 +790,12 @@ describe('Dropdown', () => { await elementUpdated(dropDown); expect(dropDown.open).to.be.true; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.secondCall).to.be.null; + expect(spy.mock.calls).lengthOf(1); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); - eventSpy.resetHistory(); + spy.mockClear(); // With selection dropDown.addEventListener('igcClosing', (e) => e.preventDefault(), { @@ -793,13 +809,17 @@ describe('Dropdown', () => { await elementUpdated(dropDown); expect(dropDown.open).to.be.true; - expect(eventSpy.firstCall).calledWith('igcChange'); - expect(eventSpy.secondCall).calledWith('igcClosing'); - expect(eventSpy.thirdCall).to.be.null; + expect(spy.mock.calls).lengthOf(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcChange', { + detail: dropDown.selectedItem, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosing', { + cancelable: true, + }); }); it('can halt closing event sequence on outside click', async () => { - const eventSpy = spy(dropDown, 'emitEvent'); + const spy = vi.spyOn(dropDown, 'emitEvent'); await openDropdown(); @@ -811,8 +831,10 @@ describe('Dropdown', () => { await elementUpdated(dropDown); expect(dropDown.open).to.be.true; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.secondCall).to.be.null; + expect(spy.mock.calls).lengthOf(1); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); }); }); diff --git a/src/components/expansion-panel/expansion-panel.spec.ts b/src/components/expansion-panel/expansion-panel.spec.ts index 0461392a0..f06ed74c1 100644 --- a/src/components/expansion-panel/expansion-panel.spec.ts +++ b/src/components/expansion-panel/expansion-panel.spec.ts @@ -1,12 +1,12 @@ import { - elementUpdated, + beforeAll, + beforeEach, + describe, expect, - fixture, - html, - waitUntil, -} from '@open-wc/testing'; -import { spy } from 'sinon'; - + it, + type MockInstance, + vi, +} from 'vitest'; import { altKey, arrowDown, @@ -15,7 +15,17 @@ import { spaceBar, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; -import { simulateClick, simulateKeyboard } from '../common/utils.spec.js'; +import { + elementUpdated, + fixture, + html, + waitUntil, +} from '../common/helpers.spec.js'; +import { + eventMatch, + simulateClick, + simulateKeyboard, +} from '../common/utils.spec.js'; import type IgcIconComponent from '../icon/icon.js'; import IgcExpansionPanelComponent from './expansion-panel.js'; @@ -29,12 +39,12 @@ type ExpansionSlots = type ExpansionParts = 'header' | 'title' | 'subtitle' | 'content' | 'indicator'; describe('Expansion Panel', () => { - before(() => { + beforeAll(() => { defineComponents(IgcExpansionPanelComponent); }); let panel: IgcExpansionPanelComponent; - let eventSpy: ReturnType; + let spy: MockInstance; const getSlot = (name: ExpansionSlots = '') => { return panel.shadowRoot!.querySelector( @@ -58,12 +68,17 @@ describe('Expansion Panel', () => { state: { open: boolean } = { open: false } ) => { expect(panel.open).to.equal(state.open); - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith( + expect(spy.mock.calls).lengthOf(2); + expect(spy).toHaveBeenNthCalledWith( + 1, state.open ? 'igcOpening' : 'igcClosing', - { cancelable: true, detail: panel } + { + cancelable: true, + detail: panel, + } ); - expect(eventSpy.secondCall).calledWith( + expect(spy).toHaveBeenNthCalledWith( + 2, state.open ? 'igcOpened' : 'igcClosed', { detail: panel, @@ -280,21 +295,21 @@ describe('Expansion Panel', () => { describe('User interactions', () => { beforeEach(async () => { panel = await fixture(createDefaultPanel()); - eventSpy = spy(panel, 'emitEvent'); + spy = vi.spyOn(panel, 'emitEvent'); }); it('should expand/collapse on header click', async () => { const header = getDOMPart('header'); simulateClick(header); - await waitUntil(() => eventSpy.calledWith('igcOpened')); + await waitUntil(() => eventMatch(spy, 'igcOpened')); verifyStateAndEventSequence({ open: true }); - eventSpy.resetHistory(); + spy.mockClear(); simulateClick(header); - await waitUntil(() => eventSpy.calledWith('igcClosed')); + await waitUntil(() => eventMatch(spy, 'igcClosed')); verifyStateAndEventSequence({ open: false }); }); @@ -303,14 +318,14 @@ describe('Expansion Panel', () => { const header = getDOMPart('header'); simulateKeyboard(header, spaceBar); - await waitUntil(() => eventSpy.calledWith('igcOpened')); + await waitUntil(() => eventMatch(spy, 'igcOpened')); verifyStateAndEventSequence({ open: true }); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(header, enterKey); - await waitUntil(() => eventSpy.calledWith('igcClosed')); + await waitUntil(() => eventMatch(spy, 'igcClosed')); verifyStateAndEventSequence({ open: false }); }); @@ -319,14 +334,14 @@ describe('Expansion Panel', () => { const header = getDOMPart('header'); simulateKeyboard(header, [altKey, arrowDown]); - await waitUntil(() => eventSpy.calledWith('igcOpened')); + await waitUntil(() => eventMatch(spy, 'igcOpened')); verifyStateAndEventSequence({ open: true }); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(header, [altKey, arrowUp]); - await waitUntil(() => eventSpy.calledWith('igcClosed')); + await waitUntil(() => eventMatch(spy, 'igcClosed')); verifyStateAndEventSequence({ open: false }); }); @@ -341,46 +356,46 @@ describe('Expansion Panel', () => { await elementUpdated(panel); expect(panel.open).to.be.false; - expect(eventSpy.callCount).to.equal(0); + expect(spy.mock.calls).lengthOf(0); simulateClick(header); await elementUpdated(panel); expect(panel.open).to.be.false; - expect(eventSpy.callCount).to.equal(0); + expect(spy.mock.calls).lengthOf(0); }); }); describe('Events', () => { beforeEach(async () => { panel = await fixture(createDefaultPanel()); - eventSpy = spy(panel, 'emitEvent'); + spy = vi.spyOn(panel, 'emitEvent'); }); it('does not emit duplicate events for expanded/collapsed state on Alt + Arrow keys', async () => { const header = getDOMPart('header'); simulateKeyboard(header, [altKey, arrowDown]); - await waitUntil(() => eventSpy.calledWith('igcOpened')); + await waitUntil(() => eventMatch(spy, 'igcOpened')); verifyStateAndEventSequence({ open: true }); simulateKeyboard(header, [altKey, arrowDown]); await elementUpdated(panel); - expect(eventSpy.callCount).to.equal(2); + expect(spy.mock.calls).lengthOf(2); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(header, [altKey, arrowUp]); - await waitUntil(() => eventSpy.calledWith('igcClosed')); + await waitUntil(() => eventMatch(spy, 'igcClosed')); verifyStateAndEventSequence({ open: false }); simulateKeyboard(header, [altKey, arrowUp]); await elementUpdated(panel); - expect(eventSpy.callCount).to.equal(2); + expect(spy.mock.calls).lengthOf(2); }); it('should be able to cancel -ing events', async () => { @@ -394,12 +409,12 @@ describe('Expansion Panel', () => { await elementUpdated(panel); expect(panel.open).to.be.false; - expect(eventSpy).calledOnceWith('igcOpening', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcOpening', { cancelable: true, detail: panel, }); - eventSpy.resetHistory(); + spy.mockClear(); panel.open = true; await elementUpdated(panel); @@ -412,7 +427,7 @@ describe('Expansion Panel', () => { await elementUpdated(panel); expect(panel.open).to.be.true; - expect(eventSpy).calledOnceWith('igcClosing', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcClosing', { cancelable: true, detail: panel, }); diff --git a/src/components/file-input/file-input.spec.ts b/src/components/file-input/file-input.spec.ts index 446ee98bb..536d64891 100644 --- a/src/components/file-input/file-input.spec.ts +++ b/src/components/file-input/file-input.spec.ts @@ -1,7 +1,7 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; import type { TemplateResult } from 'lit'; -import { spy } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { first } from '../common/util.js'; import { createFormAssociatedTestBed } from '../common/utils.spec.js'; import { @@ -16,7 +16,7 @@ describe('File Input component', () => { new File(['image data'], 'image.png', { type: 'image/png' }), ]; - before(() => { + beforeAll(() => { defineComponents(IgcFileInputComponent); }); @@ -151,23 +151,23 @@ describe('File Input component', () => { it('emits igcChange', async () => { await createFixture(html``); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateFileUpload(input, [first(files)]); await elementUpdated(element); - expect(eventSpy).calledWith('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: input.files, }); }); it('emits igcCancel', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); input.dispatchEvent(new Event('cancel', { bubbles: true })); await elementUpdated(element); - expect(eventSpy).calledOnceWith('igcCancel', { + expect(spy).toHaveBeenCalledWith('igcCancel', { detail: input.files, }); }); diff --git a/src/components/focus-trap/focus-trap.spec.ts b/src/components/focus-trap/focus-trap.spec.ts index fb8abf1fb..9fa742580 100644 --- a/src/components/focus-trap/focus-trap.spec.ts +++ b/src/components/focus-trap/focus-trap.spec.ts @@ -1,14 +1,14 @@ -import { expect, fixture, html } from '@open-wc/testing'; - +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import IgcButtonComponent from '../button/button.js'; import IgcCalendarComponent from '../calendar/calendar.js'; import type IgcDaysViewComponent from '../calendar/days-view/days-view.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { fixture, html } from '../common/helpers.spec.js'; import IgcInputComponent from '../input/input.js'; import IgcFocusTrapComponent from './focus-trap.js'; describe('Focus trap', () => { - before(() => + beforeAll(() => defineComponents( IgcFocusTrapComponent, IgcCalendarComponent, diff --git a/src/components/icon/icon.spec.ts b/src/components/icon/icon.spec.ts index 45af71ef1..188bea147 100644 --- a/src/components/icon/icon.spec.ts +++ b/src/components/icon/icon.spec.ts @@ -1,13 +1,19 @@ +import { + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { aTimeout, elementUpdated, - expect, fixture, html, -} from '@open-wc/testing'; -import { stub } from 'sinon'; - -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { first, last } from '../common/util.js'; import IgcIconComponent from './icon.js'; import { @@ -39,14 +45,12 @@ const searchSvgContent = const searchSvg = `${searchSvgContent}`; function mockResponse() { - const response = new globalThis.Response(bugSvg, { + return new globalThis.Response(bugSvg, { status: 200, headers: { 'Content-Type': 'image/svg+xml', }, }); - - return Promise.resolve(response); } describe('Icon registry', () => { @@ -54,8 +58,7 @@ describe('Icon registry', () => { const collection = 'test-collection'; beforeEach(() => { - const mocked = stub(globalThis, 'fetch'); - mocked.onCall(0).returns(mockResponse()); + vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(mockResponse()); }); it('is registered', async () => { @@ -82,7 +85,7 @@ describe('Icon registry', () => { }); describe('Referential Icons', () => { - before(() => { + beforeAll(() => { defineComponents(IgcIconComponent); }); @@ -127,7 +130,7 @@ describe('Icon registry', () => { }); afterEach(() => { - (globalThis.fetch as any).restore(); + vi.restoreAllMocks(); }); }); @@ -162,7 +165,7 @@ describe('Icon broadcast service', () => { const iconName = 'bug'; registerIconFromText(iconName, bugSvg, collectionName); - await aTimeout(0); + await aTimeout(50); const { actionType, collections } = first(events).data; expect(actionType).to.equal(ActionType.RegisterIcon); @@ -182,7 +185,7 @@ describe('Icon broadcast service', () => { for (const each of icons) { registerIconFromText(each[0], each[1], collectionName); } - await aTimeout(0); + await aTimeout(50); expect(events).lengthOf(icons.length); for (const [idx, event] of events.entries()) { @@ -206,7 +209,7 @@ describe('Icon broadcast service', () => { name: 'reference-test', collection: collectionName, }); - await aTimeout(0); + await aTimeout(50); const { actionType, collections, references } = last(events).data; @@ -226,7 +229,7 @@ describe('Icon broadcast service', () => { meta.name = 'reference-test'; meta.collection = collectionName; setIconRef(refName, refCollectionName, meta); - await aTimeout(0); + await aTimeout(50); const { actionType, collections, references } = last(events).data; @@ -249,7 +252,7 @@ describe('Icon broadcast service', () => { }, overwrite: true, }); - await aTimeout(0); + await aTimeout(50); expect(events.length).to.equal(0); }); @@ -264,7 +267,7 @@ describe('Icon broadcast service', () => { // a peer is requesting a state sync channel.postMessage({ actionType: ActionType.SyncState }); - await aTimeout(20); + await aTimeout(200); // all icon broadcasts must respond with their state // 2 from broadcast service + 1 from global. @@ -285,7 +288,7 @@ describe('Icon broadcast service', () => { // a peer is requesting a state sync channel.postMessage({ actionType: ActionType.SyncState }); - await aTimeout(0); + await aTimeout(50); expect(events).lengthOf(2); // [ActionType.RegisterIcon, ActionType.SyncState] @@ -310,7 +313,7 @@ describe('Icon broadcast service', () => { // a peer is requesting a state sync channel.postMessage({ actionType: ActionType.SyncState }); - await aTimeout(0); + await aTimeout(50); expect(events).lengthOf(1); // [ActionType.SyncState] @@ -322,7 +325,7 @@ describe('Icon broadcast service', () => { }); describe('Icon component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcIconComponent); }); diff --git a/src/components/input/input.spec.ts b/src/components/input/input.spec.ts index 9f79262ff..862919e2d 100644 --- a/src/components/input/input.spec.ts +++ b/src/components/input/input.spec.ts @@ -1,14 +1,13 @@ +import type { TemplateResult } from 'lit'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { configureTheme } from '../../theming/config.js'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, nextFrame, -} from '@open-wc/testing'; -import type { TemplateResult } from 'lit'; -import { spy } from 'sinon'; -import { configureTheme } from '../../theming/config.js'; -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, isFocused, @@ -21,7 +20,7 @@ import { import IgcInputComponent from './input.js'; describe('Input component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcInputComponent); }); @@ -280,22 +279,26 @@ describe('Input component', () => { }); it('emits igcInput', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateInput(input, { value: '123' }); await elementUpdated(element); - expect(eventSpy).calledOnceWithExactly('igcInput', { detail: '123' }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { + detail: '123', + }); }); it('emits igcChange', async () => { simulateInput(input, { value: '123' }); await elementUpdated(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); input.dispatchEvent(new Event('change')); - expect(eventSpy).calledOnceWithExactly('igcChange', { detail: '123' }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { + detail: '123', + }); }); }); }); diff --git a/src/components/list/list.spec.ts b/src/components/list/list.spec.ts index 537df6706..25311ef9a 100644 --- a/src/components/list/list.spec.ts +++ b/src/components/list/list.spec.ts @@ -1,10 +1,10 @@ -import { expect, fixture, html } from '@open-wc/testing'; - +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { fixture, html } from '../common/helpers.spec.js'; import IgcListComponent from './list.js'; describe('List', () => { - before(() => { + beforeAll(() => { defineComponents(IgcListComponent); }); diff --git a/src/components/mask-input/mask-input.spec.ts b/src/components/mask-input/mask-input.spec.ts index c5a87e866..91edbba0d 100644 --- a/src/components/mask-input/mask-input.spec.ts +++ b/src/components/mask-input/mask-input.spec.ts @@ -1,7 +1,6 @@ -import { elementUpdated, expect, fixture } from '@open-wc/testing'; -import { html } from 'lit'; -import { spy } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, simulateInput, @@ -16,7 +15,7 @@ import IgcMaskInputComponent from './mask-input.js'; import { MaskParser } from './mask-parser.js'; describe('Masked input', () => { - before(() => defineComponents(IgcMaskInputComponent)); + beforeAll(() => defineComponents(IgcMaskInputComponent)); const parser = new MaskParser(); const defaultPrompt = '_'; @@ -269,24 +268,26 @@ describe('Masked input', () => { it('igcChange event', async () => { syncParser(); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); element.value = 'abc'; await elementUpdated(element); input.dispatchEvent(new Event('change')); - expect(eventSpy).calledWith('igcChange', { detail: 'abc' }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { + detail: 'abc', + }); }); it('igcChange event with literals', async () => { syncParser(); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); element.value = 'abc'; element.valueMode = 'withFormatting'; await elementUpdated(element); input.dispatchEvent(new Event('change')); - expect(eventSpy).calledWith('igcChange', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { detail: parser.apply(element.value), }); }); @@ -296,17 +297,18 @@ describe('Masked input', () => { await elementUpdated(element); syncParser(); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); element.value = '111'; element.setSelectionRange(2, 3); await elementUpdated(element); - // fireInputEvent(input, 'insertText'); simulateInput(input, { inputType: 'insertText', skipValueProperty: true, }); - expect(eventSpy).calledWith('igcInput', { detail: '111' }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { + detail: '111', + }); }); it('igInput event (end of pattern)', async () => { @@ -314,7 +316,7 @@ describe('Masked input', () => { await elementUpdated(element); syncParser(); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); element.value = '111'; element.setSelectionRange(3, 3); await elementUpdated(element); @@ -323,7 +325,7 @@ describe('Masked input', () => { inputType: 'insertText', skipValueProperty: true, }); - expect(eventSpy).not.calledWith('igcInput', { detail: '111' }); + expect(spy).not.toHaveBeenCalledWith('igcInput', { detail: '111' }); }); it('is accessible', async () => { diff --git a/src/components/mask-input/mask-parser.spec.ts b/src/components/mask-input/mask-parser.spec.ts index ac20b2309..d3634dfb7 100644 --- a/src/components/mask-input/mask-parser.spec.ts +++ b/src/components/mask-input/mask-parser.spec.ts @@ -1,5 +1,4 @@ -import { expect } from '@open-wc/testing'; - +import { beforeEach, describe, expect, it } from 'vitest'; import { MaskParser } from './mask-parser.js'; describe('Mask parser', () => { diff --git a/src/components/nav-drawer/nav-drawer.spec.ts b/src/components/nav-drawer/nav-drawer.spec.ts index 9247367df..ac0f41ba0 100644 --- a/src/components/nav-drawer/nav-drawer.spec.ts +++ b/src/components/nav-drawer/nav-drawer.spec.ts @@ -1,12 +1,12 @@ -import { expect, fixture, html } from '@open-wc/testing'; import type { TemplateResult } from 'lit'; - +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { fixture, html } from '../common/helpers.spec.js'; import IgcIconComponent from '../icon/icon.js'; import IgcNavDrawerComponent from './nav-drawer.js'; describe('Navigation Drawer', () => { - before(() => { + beforeAll(() => { defineComponents(IgcNavDrawerComponent, IgcIconComponent); }); diff --git a/src/components/navbar/navbar.spec.ts b/src/components/navbar/navbar.spec.ts index 662c10301..030bfcea5 100644 --- a/src/components/navbar/navbar.spec.ts +++ b/src/components/navbar/navbar.spec.ts @@ -1,9 +1,10 @@ -import { expect, fixture, html } from '@open-wc/testing'; - -import { defineComponents, IgcNavbarComponent } from '../../index.js'; +import { beforeAll, describe, expect, it } from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; +import { fixture, html } from '../common/helpers.spec.js'; +import IgcNavbarComponent from './navbar.js'; describe('Navbar component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcNavbarComponent); }); diff --git a/src/components/popover/popover.spec.ts b/src/components/popover/popover.spec.ts index a4c534826..5e1ce5ab5 100644 --- a/src/components/popover/popover.spec.ts +++ b/src/components/popover/popover.spec.ts @@ -1,12 +1,11 @@ +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, nextFrame, -} from '@open-wc/testing'; - -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import IgcPopoverComponent from './popover.js'; async function waitForPaint(popover: IgcPopoverComponent) { @@ -52,7 +51,7 @@ function createNonSlottedPopover(isOpen = false) { } describe('Popover', () => { - before(() => { + beforeAll(() => { defineComponents(IgcPopoverComponent); }); diff --git a/src/components/progress/circular-progress.spec.ts b/src/components/progress/circular-progress.spec.ts index 1e8caf19f..8ec052b7a 100644 --- a/src/components/progress/circular-progress.spec.ts +++ b/src/components/progress/circular-progress.spec.ts @@ -1,11 +1,11 @@ +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, nextFrame, -} from '@open-wc/testing'; -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { first } from '../common/util.js'; import IgcCircularGradientComponent from './circular-gradient.js'; import IgcCircularProgressComponent from './circular-progress.js'; @@ -24,7 +24,7 @@ describe('Circular progress component', () => { await nextFrame(); }; - before(() => { + beforeAll(() => { defineComponents(IgcCircularProgressComponent); }); diff --git a/src/components/progress/linear-progress.spec.ts b/src/components/progress/linear-progress.spec.ts index 242668aea..884c7270f 100644 --- a/src/components/progress/linear-progress.spec.ts +++ b/src/components/progress/linear-progress.spec.ts @@ -1,11 +1,11 @@ +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, nextFrame, -} from '@open-wc/testing'; -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { first } from '../common/util.js'; import IgcLinearProgressComponent from './linear-progress.js'; @@ -23,7 +23,7 @@ describe('Linear progress component', () => { await nextFrame(); }; - before(() => { + beforeAll(() => { defineComponents(IgcLinearProgressComponent); }); diff --git a/src/components/radio-group/radio-group.spec.ts b/src/components/radio-group/radio-group.spec.ts index 2da7f1a2b..9d7e6ad55 100644 --- a/src/components/radio-group/radio-group.spec.ts +++ b/src/components/radio-group/radio-group.spec.ts @@ -1,6 +1,12 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import { spy } from 'sinon'; - +import { + beforeAll, + beforeEach, + describe, + expect, + it, + type MockInstance, + vi, +} from 'vitest'; import { arrowDown, arrowLeft, @@ -8,13 +14,18 @@ import { arrowUp, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { first, last } from '../common/util.js'; -import { isFocused, simulateKeyboard } from '../common/utils.spec.js'; +import { + expectCalledWith, + isFocused, + simulateKeyboard, +} from '../common/utils.spec.js'; import IgcRadioComponent from '../radio/radio.js'; import IgcRadioGroupComponent from './radio-group.js'; describe('Radio Group Component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcRadioGroupComponent); }); @@ -94,7 +105,7 @@ describe('Radio Group Component', () => { }); describe('Keyboard navigation', () => { - let spies: unknown[]; + let spies: MockInstance[]; async function waitForUpdate() { await Promise.all(radios.map((radio) => elementUpdated(radio))); @@ -109,7 +120,7 @@ describe('Radio Group Component', () => { radios = Array.from( group.querySelectorAll(IgcRadioComponent.tagName) ); - spies = radios.map((radio) => spy(radio, 'emitEvent')); + spies = radios.map((radio) => vi.spyOn(radio, 'emitEvent')); }); it('should be able to navigate through radios using arrow keys', async () => { @@ -121,7 +132,7 @@ describe('Radio Group Component', () => { validateGroupSelected(first); expect(isFocused(first)).to.be.true; - expect(firstSpy).calledWith('igcChange'); + expectCalledWith(firstSpy, 'igcChange'); simulateKeyboard(first, arrowDown); await waitForUpdate(); @@ -129,7 +140,7 @@ describe('Radio Group Component', () => { validateGroupSelected(second); expect(isFocused(first)).to.be.false; expect(isFocused(second)).to.be.true; - expect(secondSpy).calledWith('igcChange'); + expectCalledWith(secondSpy, 'igcChange'); simulateKeyboard(second, arrowUp); await waitForUpdate(); @@ -137,7 +148,7 @@ describe('Radio Group Component', () => { validateGroupSelected(first); expect(isFocused(second)).to.be.false; expect(isFocused(first)).to.be.true; - expect(firstSpy).to.be.calledWith('igcChange'); + expectCalledWith(firstSpy, 'igcChange'); simulateKeyboard(first, arrowRight); await waitForUpdate(); @@ -145,7 +156,7 @@ describe('Radio Group Component', () => { validateGroupSelected(second); expect(isFocused(first)).to.be.false; expect(isFocused(second)).to.be.true; - expect(secondSpy).to.be.calledWith('igcChange'); + expectCalledWith(secondSpy, 'igcChange'); simulateKeyboard(second, arrowLeft); await waitForUpdate(); @@ -153,7 +164,7 @@ describe('Radio Group Component', () => { validateGroupSelected(first); expect(isFocused(second)).to.be.false; expect(isFocused(first)).to.be.true; - expect(firstSpy).to.be.calledWith('igcChange'); + expectCalledWith(firstSpy, 'igcChange'); simulateKeyboard(first, arrowLeft); await waitForUpdate(); @@ -161,7 +172,7 @@ describe('Radio Group Component', () => { validateGroupSelected(third); expect(isFocused(first)).to.be.false; expect(isFocused(third)).to.be.true; - expect(thirdSpy).to.be.calledWith('igcChange'); + expectCalledWith(thirdSpy, 'igcChange'); simulateKeyboard(third, arrowDown); await waitForUpdate(); @@ -169,7 +180,7 @@ describe('Radio Group Component', () => { validateGroupSelected(first); expect(isFocused(third)).to.be.false; expect(isFocused(first)).to.be.true; - expect(firstSpy).to.be.calledWith('igcChange'); + expectCalledWith(firstSpy, 'igcChange'); }); it('should skip disabled radios when navigating', async () => { @@ -190,8 +201,8 @@ describe('Radio Group Component', () => { validateGroupSelected(third); expect(isFocused(first)).to.be.false; expect(isFocused(third)).to.be.true; - expect(secondSpy).to.not.be.called; - expect(thirdSpy).calledWith('igcChange'); + expect(secondSpy).not.toHaveBeenCalled(); + expectCalledWith(thirdSpy, 'igcChange'); }); }); }); diff --git a/src/components/radio/radio.spec.ts b/src/components/radio/radio.spec.ts index b0121d3ea..8d3306b58 100644 --- a/src/components/radio/radio.spec.ts +++ b/src/components/radio/radio.spec.ts @@ -1,6 +1,6 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import { spy } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { first, last } from '../common/util.js'; import { createFormAssociatedTestBed, @@ -14,7 +14,7 @@ import { import IgcRadioComponent from './radio.js'; describe('Radio Component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcRadioComponent); }); @@ -144,11 +144,11 @@ describe('Radio Component', () => { }); it('should emit igcChange event when radio is checked', async () => { - const eventSpy = spy(radio, 'emitEvent'); + const spy = vi.spyOn(radio, 'emitEvent'); radio.click(); await elementUpdated(radio); - expect(eventSpy).calledWithExactly('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: { checked: true, value: undefined, @@ -157,15 +157,16 @@ describe('Radio Component', () => { }); it('it should not emit igcChange event on already checked radio', async () => { - const eventSpy = spy(radio, 'emitEvent'); + const spy = vi.spyOn(radio, 'emitEvent'); radio.click(); await elementUpdated(radio); - expect(eventSpy.getCalls()).lengthOf(1); + + expect(spy).toHaveBeenCalledTimes(1); radio.click(); await elementUpdated(radio); - expect(eventSpy.getCalls()).lengthOf(1); + expect(spy).toHaveBeenCalledTimes(1); }); it('should be able to use external elements as label', async () => { @@ -181,13 +182,13 @@ describe('Radio Component', () => { }); it('should emit click event only once', async () => { - const eventSpy = spy(radio, 'click'); + const spy = vi.spyOn(radio, 'click'); - radio.addEventListener('click', eventSpy); + radio.addEventListener('click', spy); simulateClick(radio); await elementUpdated(radio); - expect(eventSpy.callCount).to.equal(1); + expect(spy).toHaveBeenCalledTimes(1); }); it('should update the validity state of the group when a single radio is validated', async () => { diff --git a/src/components/rating/rating.spec.ts b/src/components/rating/rating.spec.ts index 0639d01f3..53f44f065 100644 --- a/src/components/rating/rating.spec.ts +++ b/src/components/rating/rating.spec.ts @@ -1,13 +1,5 @@ -import { - elementUpdated, - expect, - fixture, - fixtureCleanup, - html, -} from '@open-wc/testing'; -import { nothing } from 'lit'; -import { spy } from 'sinon'; - +import { html, nothing } from 'lit'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { arrowDown, arrowLeft, @@ -17,6 +9,7 @@ import { homeKey, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { cleanup, elementUpdated, fixture } from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, simulateClick, @@ -27,7 +20,7 @@ import IgcRatingComponent from './rating.js'; import IgcRatingSymbolComponent from './rating-symbol.js'; describe('Rating component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcRatingComponent); }); @@ -219,7 +212,7 @@ describe('Rating component', () => { expect(symbol.getClientRects()).to.not.be.empty; }); - fixtureCleanup(); + cleanup(); rating = await fixture(renderRating(true)); expect(getProjectedSymbols(rating)).to.have.lengthOf(3); @@ -284,17 +277,17 @@ describe('Rating component', () => { }); it('correctly updates value on click', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); const symbol = getRatingSymbols(el).item(2); const { x, width } = getBoundingRect(symbol); simulateClick(symbol, { clientX: x + width / 2 }); - expect(eventSpy).calledOnceWithExactly('igcChange', { detail: 3 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: 3 }); expect(el.value).to.equal(3); }); it('correctly updates value on click [precision != 1]', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); el.step = 0.5; await elementUpdated(el); @@ -302,12 +295,12 @@ describe('Rating component', () => { const { x, width } = getBoundingRect(symbol); simulateClick(symbol, { clientX: x + width / 4 }); - expect(eventSpy).calledOnceWithExactly('igcChange', { detail: 2.5 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: 2.5 }); expect(el.value).to.equal(2.5); }); it('rounds correctly to the next step, when the step is different than 1', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); el.step = 0.4; await elementUpdated(el); @@ -315,24 +308,24 @@ describe('Rating component', () => { const { x, width } = getBoundingRect(symbol); simulateClick(symbol, { clientX: x + width * 0.55 }); // Click 55% across the width of the symbol - expect(eventSpy).calledOnceWithExactly('igcChange', { detail: 0.8 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: 0.8 }); expect(el.value).to.equal(0.8); }); it('issue-1548 - Inaccurate value calculation when precision == 1', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); const symbol = getRatingSymbols(el).item(2); const { x, width } = getBoundingRect(symbol); // Simulate offset click when precision == 1 simulateClick(symbol, { clientX: x + width / 4 }); - expect(eventSpy).calledOnceWithExactly('igcChange', { detail: 3 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: 3 }); expect(el.value).to.equal(3); }); it('correctly reflects hover state', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); el.value = 2; el.hoverPreview = true; await elementUpdated(el); @@ -340,12 +333,12 @@ describe('Rating component', () => { const { x, width } = getBoundingRect(symbol); simulatePointerMove(symbol, { clientX: x + width / 2 }); - expect(eventSpy).calledOnceWithExactly('igcHover', { detail: 3 }); + expect(spy).toHaveBeenCalledWith('igcHover', { detail: 3 }); expect(el.value).to.equal(2); }); it('does not reset value if the same rating value is clicked with allow-reset = false', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); const symbol = getRatingSymbols(el).item(4); const { x, width } = getBoundingRect(symbol); @@ -354,11 +347,11 @@ describe('Rating component', () => { simulateClick(symbol, { clientX: x + width / 2 }); expect(el.value).to.equal(5); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); }); it('correctly resets value if the same rating value is clicked with allow-reset = true', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); const symbol = getRatingSymbols(el).item(4); const { x, width } = getBoundingRect(symbol); @@ -369,40 +362,40 @@ describe('Rating component', () => { simulateClick(symbol, { clientX: x + width / 2 }); expect(el.value).to.equal(0); - expect(eventSpy).to.have.been.calledOnceWith('igcChange', { detail: 0 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: 0 }); }); it('does nothing on click if disabled', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); el.disabled = true; await elementUpdated(el); getRatingSymbols(el).item(3).click(); - expect(eventSpy).to.not.called; + expect(spy).not.toHaveBeenCalled(); expect(el.value).to.equal(0); }); it('does nothing on click if readonly', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); el.readOnly = true; await elementUpdated(el); getRatingSymbols(el).item(3).click(); await elementUpdated(el); - expect(eventSpy).to.not.be.called; + expect(spy).not.toHaveBeenCalled(); expect(el.value).to.equal(0); }); it('does nothing on keyboard interaction if readonly', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); el.readOnly = true; await elementUpdated(el); simulateKeyboard(el, arrowRight); await elementUpdated(el); - expect(eventSpy).to.not.be.called; + expect(spy).not.toHaveBeenCalled(); expect(el.value).to.equal(0); }); @@ -477,13 +470,13 @@ describe('Rating component', () => { }); it('should not emit a change event if the value is unchanged', async () => { - const eventSpy = spy(el, 'emitEvent'); + const spy = vi.spyOn(el, 'emitEvent'); el.value = 5; await elementUpdated(el); simulateKeyboard(el, arrowRight); - expect(eventSpy).to.not.be.calledOnce; + expect(spy).not.toHaveBeenCalled(); }); it('sets step to 1 if in single selection mode', async () => { diff --git a/src/components/resize-container/resize-container.spec.ts b/src/components/resize-container/resize-container.spec.ts index 29a033b7e..596f9c834 100644 --- a/src/components/resize-container/resize-container.spec.ts +++ b/src/components/resize-container/resize-container.spec.ts @@ -1,10 +1,18 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import type Sinon from 'sinon'; -import { spy } from 'sinon'; - +import { + beforeAll, + beforeEach, + describe, + expect, + it, + type MockInstance, + vi, +} from 'vitest'; import { escapeKey } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { + expectCalledWith, + expectNotCalledWith, simulateKeyboard, simulateLostPointerCapture, simulatePointerDown, @@ -12,15 +20,13 @@ import { simulatePointerLeave, simulatePointerMove, } from '../common/utils.spec.js'; -import IgcResizeContainerComponent, { - type IgcResizeContainerComponentEventMap, -} from './resize-container.js'; +import IgcResizeContainerComponent from './resize-container.js'; import type { ResizeCallbackParams } from './types.js'; describe('Resize container', () => { let element: IgcResizeContainerComponent; - before(() => { + beforeAll(() => { defineComponents(IgcResizeContainerComponent); }); @@ -113,67 +119,67 @@ describe('Resize container', () => { it('resize behavior should only start when interacting with the trigger element', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulatePointerDown(element); await elementUpdated(element); - expect(eventSpy).not.calledWith('igcResizeStart'); + expectNotCalledWith(spy, 'igcResizeStart'); }); it('should not start resize behavior with non-primary "button"', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const DOM = getDOM(element); simulatePointerDown(DOM.adorners.corner, { button: 1 }); await elementUpdated(element); - expect(eventSpy).not.calledWith('igcResizeStart'); + expectNotCalledWith(spy, 'igcResizeStart'); }); it('should not have a ghost element when in immediate mode', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const { adorners } = getDOM(element); simulatePointerDown(adorners.corner); await elementUpdated(element); - const { ghost } = getResizeEventState(eventSpy); + const { ghost } = getResizeEventState(spy); expect(ghost).is.null; }); it('should fire `resizeStart` on pointer interaction', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const { adorners } = getDOM(element); simulatePointerDown(adorners.corner); await elementUpdated(element); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(spy, 'igcResizeStart'); }); it('should not fire `resize` unless `resizeStart` is triggered', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const { adorners } = getDOM(element); simulatePointerMove(adorners.corner); await elementUpdated(element); - expect(eventSpy).not.calledWith('igcResizeStart'); + expectNotCalledWith(spy, 'igcResizeStart'); }); it('should fire `resize` when resizing behavior is triggered', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const { adorners } = getDOM(element); simulatePointerDown(adorners.corner); @@ -182,7 +188,7 @@ describe('Resize container', () => { simulatePointerMove(adorners.corner); await elementUpdated(element); - expect(eventSpy).calledWith('igcResize'); + expectCalledWith(spy, 'igcResize'); }); it('should fire `resizeEnd` when resizing is finished', async () => { @@ -193,22 +199,21 @@ describe('Resize container', () => { simulatePointerDown(adorners.corner); await elementUpdated(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateLostPointerCapture(adorners.corner); await elementUpdated(element); - expect(eventSpy).calledWith('igcResizeEnd'); + expectCalledWith(spy, 'igcResizeEnd'); }); it('should not fire `resizeCancel` when escape key is pressed without active resizing', async () => { - const eventSpy = spy(element, 'emitEvent'); - + const spy = vi.spyOn(element, 'emitEvent'); // Default state simulateKeyboard(element, escapeKey); await elementUpdated(element); - expect(eventSpy).not.calledWith('igcResizeCancel'); + expectNotCalledWith(spy, 'igcResizeCancel'); // While in "active" state but not in resize mode await setResizeActiveState(element); @@ -216,8 +221,7 @@ describe('Resize container', () => { simulateKeyboard(element, escapeKey); await elementUpdated(element); - expect(eventSpy).not.calledWith('igcResizeCancel'); - expect(eventSpy.getCalls()).to.be.empty; + expectNotCalledWith(spy, 'igcResizeCancel'); }); it('should fire `resizeCancel` when escape key is pressed during resizing', async () => { @@ -228,12 +232,12 @@ describe('Resize container', () => { simulatePointerDown(adorners.corner); await elementUpdated(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateKeyboard(element, escapeKey); await elementUpdated(element); - expect(eventSpy).calledWith('igcResizeCancel'); + expectCalledWith(spy, 'igcResizeCancel'); }); }); @@ -241,18 +245,16 @@ describe('Resize container', () => { it('`resizeStart` event parameters match initial state', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const DOM = getDOM(element); simulatePointerDown(DOM.adorners.corner); await elementUpdated(element); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(spy, 'igcResizeStart'); const targetRect = DOM.container.getBoundingClientRect(); - const { initial, current, deltaX, deltaY } = - getResizeEventState(eventSpy); - + const { initial, current, deltaX, deltaY } = getResizeEventState(spy); // Assert the resize container DOM rect matches the event parameters state expect(initial).to.eql(targetRect); expect(current).to.eql(targetRect); @@ -264,7 +266,7 @@ describe('Resize container', () => { it('`resize` event parameters match resize target state', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const DOM = getDOM(element); simulatePointerDown(DOM.adorners.corner); @@ -281,7 +283,7 @@ describe('Resize container', () => { await elementUpdated(element); rect = DOM.container.getBoundingClientRect(); - current = getResizeEventState(eventSpy); + current = getResizeEventState(spy); // Correct deltas expect([current.deltaX, current.deltaY]).to.eql([ @@ -300,13 +302,13 @@ describe('Resize container', () => { it('`resizeEnd` event parameters match resize target end state', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const DOM = getDOM(element); simulatePointerDown(DOM.adorners.corner); await elementUpdated(element); - let state = getResizeEventState(eventSpy); + let state = getResizeEventState(spy); simulatePointerMove( DOM.adorners.corner, @@ -319,7 +321,7 @@ describe('Resize container', () => { simulateLostPointerCapture(DOM.adorners.corner); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); const rect = DOM.container.getBoundingClientRect(); expect([ @@ -336,14 +338,14 @@ describe('Resize container', () => { it('`resizeCancel` correctly restores initial state', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const DOM = getDOM(element); simulatePointerDown(DOM.adorners.corner); await elementUpdated(element); let rect: DOMRect; - let state = getResizeEventState(eventSpy); + let state = getResizeEventState(spy); simulatePointerMove( DOM.adorners.corner, @@ -355,7 +357,7 @@ describe('Resize container', () => { ); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); rect = DOM.container.getBoundingClientRect(); expect([rect.width, rect.height]).to.eql([ @@ -385,15 +387,15 @@ describe('Resize container', () => { await setResizeActiveState(element); const DOM = getDOM(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const rect = DOM.container.getBoundingClientRect(); simulatePointerDown(DOM.adorners.side); await elementUpdated(element); - let state = getResizeEventState(eventSpy); + let state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(spy, 'igcResizeStart'); expect(state.trigger).to.equal(DOM.adorners.side); simulatePointerMove(DOM.adorners.side, { @@ -402,18 +404,18 @@ describe('Resize container', () => { }); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResize'); + expectCalledWith(spy, 'igcResize'); expect(state.current.height).to.equal(rect.height); expect(state.current.width).to.equal(rect.width + 500); simulateLostPointerCapture(DOM.adorners.side); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResizeEnd'); + expectCalledWith(spy, 'igcResizeEnd'); expect(state.current).to.eql(DOM.container.getBoundingClientRect()); }); @@ -421,15 +423,15 @@ describe('Resize container', () => { await setResizeActiveState(element); const DOM = getDOM(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const rect = DOM.container.getBoundingClientRect(); simulatePointerDown(DOM.adorners.bottom); await elementUpdated(element); - let state = getResizeEventState(eventSpy); + let state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(spy, 'igcResizeStart'); expect(state.trigger).to.equal(DOM.adorners.bottom); simulatePointerMove(DOM.adorners.bottom, { @@ -438,18 +440,18 @@ describe('Resize container', () => { }); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResize'); + expectCalledWith(spy, 'igcResize'); expect(state.current.width).to.equal(rect.width); expect(state.current.height).to.equal(rect.height + 500); simulateLostPointerCapture(DOM.adorners.bottom); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResizeEnd'); + expectCalledWith(spy, 'igcResizeEnd'); expect(state.current).to.eql(DOM.container.getBoundingClientRect()); }); }); @@ -520,24 +522,24 @@ describe('Resize container', () => { it('resize behavior should only start when interacting with the trigger element', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulatePointerDown(element); await elementUpdated(element); - expect(eventSpy).not.calledWith('igcResizeStart'); + expectNotCalledWith(spy, 'igcResizeStart'); }); it('should not start resize behavior with non-primary "button"', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const DOM = getDOM(element); simulatePointerDown(DOM.adorners.corner, { button: 1 }); await elementUpdated(element); - expect(eventSpy).not.calledWith('igcResizeStart'); + expectNotCalledWith(spy, 'igcResizeStart'); }); it('should initialize the default ghost on pointerdown', async () => { @@ -574,25 +576,25 @@ describe('Resize container', () => { it('should fire `resizeStart` on pointer interaction', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const { adorners } = getDOM(element); simulatePointerDown(adorners.corner); await elementUpdated(element); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(spy, 'igcResizeStart'); }); it('should not fire `resize` unless `resizeStart` is triggered', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const { adorners } = getDOM(element); simulatePointerMove(adorners.corner); await elementUpdated(element); - expect(eventSpy).not.calledWith('igcResizeStart'); + expectNotCalledWith(spy, 'igcResizeStart'); }); it('should fire `resize` event', async () => { @@ -603,12 +605,12 @@ describe('Resize container', () => { simulatePointerDown(adorners.corner); await elementUpdated(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulatePointerMove(adorners.corner); await elementUpdated(element); - expect(eventSpy).calledWith('igcResize'); + expectCalledWith(spy, 'igcResize'); }); it('should fire `resizeEnd` when resizing is finished', async () => { @@ -619,12 +621,12 @@ describe('Resize container', () => { simulatePointerDown(adorners.corner); await elementUpdated(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateLostPointerCapture(adorners.corner); await elementUpdated(element); - expect(eventSpy).calledWith('igcResizeEnd'); + expectCalledWith(spy, 'igcResizeEnd'); }); it('should remove ghost element when resizing is done', async () => { @@ -635,23 +637,23 @@ describe('Resize container', () => { simulatePointerDown(DOM.adorners.corner); await elementUpdated(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateLostPointerCapture(DOM.adorners.corner); await elementUpdated(element); - expect(eventSpy).calledWith('igcResizeEnd'); + expectCalledWith(spy, 'igcResizeEnd'); expect(DOM.ghostElement).is.null; }); it('should not fire `resizeCancel` when escape key is pressed without active resizing', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); // Default state simulateKeyboard(element, escapeKey); await elementUpdated(element); - expect(eventSpy).not.calledWith('igcResizeCancel'); + expectNotCalledWith(spy, 'igcResizeCancel'); // While in "active" state but not in resize mode await setResizeActiveState(element); @@ -659,8 +661,8 @@ describe('Resize container', () => { simulateKeyboard(element, escapeKey); await elementUpdated(element); - expect(eventSpy).not.calledWith('igcResizeCancel'); - expect(eventSpy.getCalls()).to.be.empty; + expectNotCalledWith(spy, 'igcResizeCancel'); + expect(spy.mock.calls).to.be.empty; }); it('should fire `resizeCancel` when escape key is pressed during resizing', async () => { @@ -671,12 +673,12 @@ describe('Resize container', () => { simulatePointerDown(adorners.corner); await elementUpdated(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateKeyboard(element, escapeKey); await elementUpdated(element); - expect(eventSpy).calledWith('igcResizeCancel'); + expectCalledWith(spy, 'igcResizeCancel'); }); it('should remove ghost element on `resizeCancel`', async () => { @@ -687,12 +689,12 @@ describe('Resize container', () => { simulatePointerDown(DOM.adorners.corner); await elementUpdated(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateKeyboard(element, escapeKey); await elementUpdated(element); - expect(eventSpy).calledWith('igcResizeCancel'); + expectCalledWith(spy, 'igcResizeCancel'); expect(DOM.ghostElement).is.null; }); }); @@ -701,18 +703,16 @@ describe('Resize container', () => { it('`resizeStart` event parameters match initial state', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const DOM = getDOM(element); simulatePointerDown(DOM.adorners.corner); await elementUpdated(element); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(spy, 'igcResizeStart'); const targetRect = DOM.ghostElement.getBoundingClientRect(); - const { initial, current, deltaX, deltaY } = - getResizeEventState(eventSpy); - + const { initial, current, deltaX, deltaY } = getResizeEventState(spy); // Assert the resize container DOM rect matches the event parameters state expect(initial).to.eql(targetRect); expect(current).to.eql(targetRect); @@ -724,7 +724,7 @@ describe('Resize container', () => { it('`resize` event parameters match resize target state', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const DOM = getDOM(element); simulatePointerDown(DOM.adorners.corner); @@ -741,7 +741,7 @@ describe('Resize container', () => { await elementUpdated(element); rect = DOM.ghostElement.getBoundingClientRect(); - current = getResizeEventState(eventSpy); + current = getResizeEventState(spy); // Correct deltas expect([current.deltaX, current.deltaY]).to.eql([ @@ -760,13 +760,13 @@ describe('Resize container', () => { it('`resizeEnd` event parameters match resize target end state', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const DOM = getDOM(element); simulatePointerDown(DOM.adorners.corner); await elementUpdated(element); - let state = getResizeEventState(eventSpy); + let state = getResizeEventState(spy); simulatePointerMove( DOM.adorners.corner, @@ -779,7 +779,7 @@ describe('Resize container', () => { simulateLostPointerCapture(DOM.adorners.corner); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); const rect = DOM.container.getBoundingClientRect(); expect([ @@ -796,14 +796,14 @@ describe('Resize container', () => { it('`resizeCancel` correctly restores initial state', async () => { await setResizeActiveState(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const DOM = getDOM(element); simulatePointerDown(DOM.adorners.corner); await elementUpdated(element); let rect: DOMRect; - let state = getResizeEventState(eventSpy); + let state = getResizeEventState(spy); simulatePointerMove( DOM.adorners.corner, @@ -815,7 +815,7 @@ describe('Resize container', () => { ); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); rect = DOM.ghostElement.getBoundingClientRect(); expect([rect.width, rect.height]).to.eql([ @@ -845,16 +845,16 @@ describe('Resize container', () => { await setResizeActiveState(element); const DOM = getDOM(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const rect = DOM.container.getBoundingClientRect(); simulatePointerDown(DOM.adorners.side); await elementUpdated(element); const ghostRect = DOM.ghostElement.getBoundingClientRect(); - let state = getResizeEventState(eventSpy); + let state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(spy, 'igcResizeStart'); expect(state.trigger).to.equal(DOM.adorners.side); simulatePointerMove(DOM.adorners.side, { @@ -863,18 +863,18 @@ describe('Resize container', () => { }); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResize'); + expectCalledWith(spy, 'igcResize'); expect(state.current.height).to.equal(ghostRect.height); expect(state.current.width).to.equal(ghostRect.width + 500); simulateLostPointerCapture(DOM.adorners.side); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResizeEnd'); + expectCalledWith(spy, 'igcResizeEnd'); expect(state.current).to.eql(DOM.container.getBoundingClientRect()); }); @@ -882,16 +882,16 @@ describe('Resize container', () => { await setResizeActiveState(element); const DOM = getDOM(element); - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const rect = DOM.container.getBoundingClientRect(); simulatePointerDown(DOM.adorners.bottom); await elementUpdated(element); const ghostRect = DOM.ghostElement.getBoundingClientRect(); - let state = getResizeEventState(eventSpy); + let state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(spy, 'igcResizeStart'); expect(state.trigger).to.equal(DOM.adorners.bottom); simulatePointerMove(DOM.adorners.bottom, { @@ -900,18 +900,18 @@ describe('Resize container', () => { }); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResize'); + expectCalledWith(spy, 'igcResize'); expect(state.current.width).to.equal(ghostRect.width); expect(state.current.height).to.equal(ghostRect.height + 500); simulateLostPointerCapture(DOM.adorners.bottom); await elementUpdated(element); - state = getResizeEventState(eventSpy); + state = getResizeEventState(spy); - expect(eventSpy).calledWith('igcResizeEnd'); + expectCalledWith(spy, 'igcResizeEnd'); expect(state.current).to.eql(DOM.container.getBoundingClientRect()); }); }); @@ -953,14 +953,6 @@ async function setResizeActiveState( await elementUpdated(resizeElement); } -function getResizeEventState( - eventSpy: Sinon.SinonSpy< - [ - type: keyof IgcResizeContainerComponentEventMap, - eventInitDict?: CustomEventInit | undefined, - ], - boolean - > -): ResizeCallbackParams['state'] { - return eventSpy.lastCall.lastArg.detail.state; +function getResizeEventState(spy: MockInstance): ResizeCallbackParams['state'] { + return spy.mock.lastCall![1].detail.state; } diff --git a/src/components/ripple/ripple.spec.ts b/src/components/ripple/ripple.spec.ts index 9cd5540d0..a865c2add 100644 --- a/src/components/ripple/ripple.spec.ts +++ b/src/components/ripple/ripple.spec.ts @@ -1,6 +1,7 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import IgcButtonComponent from '../button/button.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { simulatePointerDown } from '../common/utils.spec.js'; import IgcRippleComponent from './ripple.js'; @@ -8,7 +9,7 @@ describe('Ripple', () => { let ripple: IgcRippleComponent; let button: IgcButtonComponent; - before(() => { + beforeAll(() => { defineComponents(IgcRippleComponent, IgcButtonComponent); }); diff --git a/src/components/select/select.spec.ts b/src/components/select/select.spec.ts index 5a7a7fd54..171fdc39a 100644 --- a/src/components/select/select.spec.ts +++ b/src/components/select/select.spec.ts @@ -1,5 +1,4 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; -import { spy, useFakeTimers } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { altKey, arrowDown, @@ -12,8 +11,11 @@ import { tabKey, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture, html } from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, + expectCalledWith, + expectNotCalledWith, isFocused, simulateClick, simulateKeyboard, @@ -46,7 +48,7 @@ type SelectSlots = | 'toggle-icon-expanded'; describe('Select', () => { - before(() => defineComponents(IgcSelectComponent)); + beforeAll(() => defineComponents(IgcSelectComponent)); let select: IgcSelectComponent; @@ -322,15 +324,17 @@ describe('Select', () => { }); it('`close` behavior', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); select.scrollStrategy = 'close'; await openSelect(); await simulateScroll(container, { top: 200 }); expect(select.open).to.be.false; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.lastCall).calledWith('igcClosed'); + expect(spy).toHaveBeenNthCalledWith(1, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosed'); }); it('`block behavior`', async () => { @@ -616,13 +620,13 @@ describe('Select', () => { }); it('keyboard selection works (closed)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); for (const item of select.items) { simulateKeyboard(select, arrowDown); await elementUpdated(select); - expect(eventSpy).calledWith('igcChange', { detail: item }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: item }); checkItemState(item, { selected: true }); } }); @@ -834,13 +838,13 @@ describe('Select', () => { // Search selection it('does not select disabled items when searching (closed state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); simulateKeyboard(select, 'tes'.split('')); await elementUpdated(select); const item = select.items[2]; - expect(eventSpy).not.calledWith('igcChange', { detail: item }); + expect(spy).not.toHaveBeenCalledWith('igcChange', { detail: item }); checkItemState(item, { active: false, selected: false }); expect(select.value).to.be.undefined; @@ -848,14 +852,14 @@ describe('Select', () => { }); it('does not activates disabled items when searching (open state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); await openSelect(); simulateKeyboard(select, 'tes'.split('')); await elementUpdated(select); const item = select.items[2]; - expect(eventSpy).not.calledWith('igcChange', { detail: item }); + expect(spy).not.toHaveBeenCalledWith('igcChange', { detail: item }); checkItemState(item, { active: false, selected: false }); expect(select.value).to.be.undefined; @@ -863,31 +867,31 @@ describe('Select', () => { }); it('resumes search after default timeout', async () => { - const clock = useFakeTimers({ now: 0, toFake: ['Date'] }); + vi.useFakeTimers({ now: 0, toFake: ['Date'] }); simulateKeyboard(select, 'null'.split('')); await elementUpdated(select); expect(select.selectedItem).to.be.null; - await clock.tickAsync(501); + await vi.advanceTimersByTimeAsync(501); simulateKeyboard(select, 'impl'.split('')); await elementUpdated(select); expect(select.selectedItem?.value).to.equal('implementation'); - clock.restore(); + vi.useRealTimers(); }); it('activates the correct item when searching with character keys (open state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); await openSelect(); simulateKeyboard(select, 'doc'.split('')); await elementUpdated(select); const item = select.items.at(-2)!; - expect(eventSpy).not.calledWith('igcChange', { detail: item }); + expect(spy).not.toHaveBeenCalledWith('igcChange', { detail: item }); checkItemState(item, { active: true, selected: false }); expect(select.value).to.be.undefined; @@ -896,13 +900,13 @@ describe('Select', () => { }); it('selects the correct item when searching (closed state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); simulateKeyboard(select, 'doc'.split('')); await elementUpdated(select); const item = select.items.at(-2)!; - expect(eventSpy).calledWith('igcChange', { detail: item }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: item }); checkItemState(item, { active: true, selected: true }); expect(select.value).to.equal(item.value); @@ -929,13 +933,15 @@ describe('Select', () => { }); it('Home key (closed state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); simulateKeyboard(select, homeKey); await elementUpdated(select); const item = select.items[0]; - expect(eventSpy).calledOnceWith('igcChange', { detail: item }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { + detail: item, + }); checkItemState(item, { active: true, selected: true }); expect(select.value).to.equal(item.value); @@ -944,14 +950,14 @@ describe('Select', () => { }); it('Home key (open state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); await openSelect(); simulateKeyboard(select, homeKey); await elementUpdated(select); const item = select.items[0]; - expect(eventSpy).not.calledWith('igcChange', { detail: item }); + expect(spy).not.toHaveBeenCalledWith('igcChange', { detail: item }); checkItemState(item, { active: true, selected: false }); expect(select.value).to.be.undefined; @@ -960,13 +966,15 @@ describe('Select', () => { }); it('End key (closed state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); simulateKeyboard(select, endKey); await elementUpdated(select); const item = select.items.at(-2)!; - expect(eventSpy).calledOnceWith('igcChange', { detail: item }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { + detail: item, + }); checkItemState(item, { active: true, selected: true }); expect(select.value).to.equal(item.value); @@ -975,14 +983,14 @@ describe('Select', () => { }); it('End key (open state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); await openSelect(); simulateKeyboard(select, endKey); await elementUpdated(select); const item = select.items.at(-2)!; - expect(eventSpy).not.calledWith('igcChange', { detail: item }); + expect(spy).not.toHaveBeenCalledWith('igcChange', { detail: item }); checkItemState(item, { active: true, selected: false }); expect(select.value).to.be.undefined; @@ -991,7 +999,7 @@ describe('Select', () => { }); it('ArrowDown (closed state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); const activeItems = Items.filter((item) => !item.disabled); const { value: lastValue } = activeItems.at(-1)!; @@ -1000,7 +1008,9 @@ describe('Select', () => { simulateKeyboard(select, arrowDown); await elementUpdated(select); - expect(eventSpy).calledWith('igcChange', { detail: getActiveItem() }); + expect(spy).toHaveBeenCalledWith('igcChange', { + detail: getActiveItem(), + }); expect(getActiveItem()?.value).to.equal(value); expect(select.value).to.equal(value); expect(select.selectedItem).to.equal(getActiveItem()); @@ -1010,14 +1020,14 @@ describe('Select', () => { simulateKeyboard(select, arrowDown); await elementUpdated(select); - expect(eventSpy.callCount).to.equal(activeItems.length); + expect(spy).toHaveBeenCalledTimes(activeItems.length); expect(getActiveItem()?.value).to.equal(lastValue); expect(select.value).to.equal(lastValue); expect(select.selectedItem?.value).to.equal(lastValue); }); it('ArrowDown (open state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); const activeItems = Items.filter((item) => !item.disabled); await openSelect(); @@ -1026,7 +1036,7 @@ describe('Select', () => { simulateKeyboard(select, arrowDown); await elementUpdated(select); - expect(eventSpy).not.calledWith('igChange'); + expectNotCalledWith(spy, 'igcChange'); expect(getActiveItem()?.value).to.equal(value); expect(select.value).to.be.undefined; expect(select.selectedItem).to.be.null; @@ -1036,14 +1046,14 @@ describe('Select', () => { simulateKeyboard(select, arrowDown); await elementUpdated(select); - expect(eventSpy).not.calledWith('igChange'); + expectNotCalledWith(spy, 'igcChange'); expect(getActiveItem()?.value).to.equal(activeItems.at(-1)?.value); expect(select.value).to.be.undefined; expect(select.selectedItem).to.be.null; }); it('ArrowUp (closed state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); const activeItems = Items.filter((item) => !item.disabled).reverse(); const { value: lastValue } = activeItems.at(-1)!; @@ -1054,7 +1064,9 @@ describe('Select', () => { simulateKeyboard(select, arrowUp); await elementUpdated(select); - expect(eventSpy).calledWith('igcChange', { detail: getActiveItem() }); + expect(spy).toHaveBeenCalledWith('igcChange', { + detail: getActiveItem(), + }); expect(getActiveItem()?.value).to.equal(value); expect(select.value).to.equal(value); expect(select.selectedItem).to.equal(getActiveItem()); @@ -1064,14 +1076,14 @@ describe('Select', () => { simulateKeyboard(select, arrowUp); await elementUpdated(select); - expect(eventSpy.callCount).to.equal(activeItems.length); + expect(spy).toHaveBeenCalledTimes(activeItems.length); expect(getActiveItem()?.value).to.equal(lastValue); expect(select.value).to.equal(lastValue); expect(select.selectedItem?.value).to.equal(lastValue); }); it('ArrowUp (open state)', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); const activeItems = Items.filter((item) => !item.disabled).reverse(); await openSelect(); @@ -1082,7 +1094,7 @@ describe('Select', () => { simulateKeyboard(select, arrowUp); await elementUpdated(select); - expect(eventSpy).not.calledWith('igChange'); + expectNotCalledWith(spy, 'igcChange'); expect(getActiveItem()?.value).to.equal(value); expect(select.value).to.be.undefined; expect(select.selectedItem).to.be.null; @@ -1092,7 +1104,7 @@ describe('Select', () => { simulateKeyboard(select, arrowUp); await elementUpdated(select); - expect(eventSpy).not.calledWith('igChange'); + expectNotCalledWith(spy, 'igcChange'); expect(getActiveItem()?.value).to.equal(activeItems.at(-1)?.value); expect(select.value).to.be.undefined; expect(select.selectedItem).to.be.null; @@ -1105,41 +1117,44 @@ describe('Select', () => { }); it('correct sequence of events', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); simulateClick(getInput()); await elementUpdated(select); - expect(eventSpy.firstCall).calledWith('igcOpening'); - expect(eventSpy.secondCall).calledWith('igcOpened'); - - eventSpy.resetHistory(); + expect(spy).toHaveBeenNthCalledWith(1, 'igcOpening', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(2, 'igcOpened'); + spy.mockClear(); simulateClick(select.items[0]); await elementUpdated(select); - expect(eventSpy.firstCall).calledWith('igcChange', { + expect(spy).toHaveBeenNthCalledWith(1, 'igcChange', { detail: select.items[0], }); - expect(eventSpy.secondCall).calledWith('igcClosing'); - expect(eventSpy.thirdCall).calledWith('igcClosed'); + expect(spy).toHaveBeenNthCalledWith(2, 'igcClosing', { + cancelable: true, + }); + expect(spy).toHaveBeenNthCalledWith(3, 'igcClosed'); }); it('does not emit events on API calls', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); await openSelect(); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); await select.hide(); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); select.select('testing'); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); }); it('can halt opening event sequence', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); select.addEventListener('igcOpening', (e) => e.preventDefault(), { once: true, }); @@ -1148,12 +1163,12 @@ describe('Select', () => { await elementUpdated(select); expect(select.open).to.be.false; - expect(eventSpy.firstCall).calledWith('igcOpening'); - expect(eventSpy.secondCall).to.be.null; + expectCalledWith(spy, 'igcOpening'); + expectNotCalledWith(spy, 'igcOpened'); }); it('can halt closing event sequence', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); select.addEventListener('igcClosing', (e) => e.preventDefault(), { once: true, }); @@ -1165,10 +1180,9 @@ describe('Select', () => { await elementUpdated(select); expect(select.open).to.be.true; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.secondCall).to.be.null; - - eventSpy.resetHistory(); + expectCalledWith(spy, 'igcClosing'); + expectNotCalledWith(spy, 'igcClosed'); + spy.mockClear(); // With selection select.addEventListener('igcClosing', (e) => e.preventDefault(), { @@ -1182,13 +1196,12 @@ describe('Select', () => { await elementUpdated(select); expect(select.open).to.be.true; - expect(eventSpy.firstCall).calledWith('igcChange'); - expect(eventSpy.secondCall).calledWith('igcClosing'); - expect(eventSpy.thirdCall).to.be.null; + expectCalledWith(spy, 'igcChange'); + expectCalledWith(spy, 'igcClosing'); }); it('can halt closing event sequence on outside click', async () => { - const eventSpy = spy(select, 'emitEvent'); + const spy = vi.spyOn(select, 'emitEvent'); select.addEventListener('igcClosing', (e) => e.preventDefault(), { once: true, }); @@ -1199,8 +1212,8 @@ describe('Select', () => { await elementUpdated(select); expect(select.open).to.be.true; - expect(eventSpy.firstCall).calledWith('igcClosing'); - expect(eventSpy.secondCall).to.be.null; + expectCalledWith(spy, 'igcClosing'); + expectNotCalledWith(spy, 'igcClosed'); }); }); diff --git a/src/components/slider/slider.spec.ts b/src/components/slider/slider.spec.ts index 1d1ae5096..5ccce3704 100644 --- a/src/components/slider/slider.spec.ts +++ b/src/components/slider/slider.spec.ts @@ -1,12 +1,4 @@ -import { - aTimeout, - elementUpdated, - expect, - fixture, - html, -} from '@open-wc/testing'; -import { spy } from 'sinon'; - +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { arrowDown, arrowLeft, @@ -18,6 +10,12 @@ import { pageUpKey, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { + aTimeout, + elementUpdated, + fixture, + html, +} from '../common/helpers.spec.js'; import { asPercent } from '../common/util.js'; import { createFormAssociatedTestBed, @@ -34,7 +32,7 @@ describe('Slider component', () => { describe('Regular', () => { let slider: IgcSliderComponent; - before(() => { + beforeAll(() => { defineComponents(IgcSliderComponent); }); @@ -122,33 +120,33 @@ describe('Slider component', () => { }); it('value should be changed when clicking and dragging the slider and corresponding events are fired', async () => { - const eventSpy = spy(slider, 'emitEvent'); + const spy = vi.spyOn(slider, 'emitEvent'); const { x, width } = slider.getBoundingClientRect(); simulatePointerDown(slider, { clientX: x + width / 2 }); await elementUpdated(slider); expect(slider.value).to.eq(50); - expect(eventSpy).calledOnceWithExactly('igcInput', { detail: 50 }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { detail: 50 }); - eventSpy.resetHistory(); + spy.mockClear(); simulatePointerMove(slider, { clientX: x + width * 0.7 }); await elementUpdated(slider); expect(slider.value).to.eq(70); - expect(eventSpy).calledOnceWithExactly('igcInput', { detail: 70 }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { detail: 70 }); - eventSpy.resetHistory(); + spy.mockClear(); simulateLostPointerCapture(slider); await elementUpdated(slider); expect(slider.value).to.eq(70); - expect(eventSpy).calledOnceWithExactly('igcChange', { detail: 70 }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { detail: 70 }); }); it('events should not be emitted once thumbs reaches end boundary even if pointer events are still fired', async () => { - const eventSpy = spy(slider, 'emitEvent'); + const spy = vi.spyOn(slider, 'emitEvent'); const { x, width } = slider.getBoundingClientRect(); const sliderCenterX = { @@ -161,7 +159,7 @@ describe('Slider component', () => { await elementUpdated(slider); expect(slider.value).to.eq(50); - expect(eventSpy).calledOnceWithExactly('igcInput', { detail: 50 }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { detail: 50 }); // Simulate 10 pointer moves with stacking delta = 1/4 of the slider's width simulatePointerMove(slider, sliderCenterX, deltaX, 10); @@ -170,18 +168,18 @@ describe('Slider component', () => { expect(slider.value).to.equal(100); // 1 igcInput for pointerDown + 2 more for each pointermove till the end - expect(eventSpy.callCount).to.equal(3); - expect(eventSpy.lastCall).calledWithExactly('igcInput', { + expect(spy).toHaveBeenCalledTimes(3); + expect(spy).toHaveBeenLastCalledWith('igcInput', { detail: 100, }); - eventSpy.resetHistory(); + spy.mockClear(); simulateLostPointerCapture(slider); await elementUpdated(slider); expect(slider.value).to.eq(100); - expect(eventSpy).calledOnceWithExactly('igcChange', { detail: 100 }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { detail: 100 }); }); it('track fill and thumb should be positioned correctly according to the current value', async () => { @@ -319,7 +317,7 @@ describe('Slider component', () => { simulatePointerDown(slider, { clientX: x + width * 0.54321 }); await elementUpdated(slider); - expect(slider.value).to.eq(54.321); + expect(slider.value).approximately(54.321, 0.0001); }); it('primary tick marks should be displayed when primaryTickMarks is greater than 0', async () => { @@ -622,7 +620,7 @@ describe('Slider component', () => { }); it('value should be increased or decreased with 1 step when pressing right/top or down/left arrow keys', async () => { - const eventSpy = spy(slider, 'emitEvent'); + const spy = vi.spyOn(slider, 'emitEvent'); slider.step = 2; slider.value = 50; await elementUpdated(slider); @@ -630,34 +628,34 @@ describe('Slider component', () => { simulateKeyboard(slider, arrowRight); await elementUpdated(slider); expect(slider.value).to.eq(52); - expect(eventSpy).to.be.calledTwice; - expect(eventSpy).calledWith('igcInput', { detail: 52 }); - expect(eventSpy).calledWith('igcChange', { detail: 52 }); + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenCalledWith('igcInput', { detail: 52 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: 52 }); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(slider, arrowLeft); await elementUpdated(slider); expect(slider.value).to.eq(50); - expect(eventSpy).calledWith('igcInput', { detail: 50 }); - expect(eventSpy).calledWith('igcChange', { detail: 50 }); + expect(spy).toHaveBeenCalledWith('igcInput', { detail: 50 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: 50 }); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(slider, arrowDown); await elementUpdated(slider); expect(slider.value).to.eq(48); - expect(eventSpy).calledWith('igcInput', { detail: 48 }); - expect(eventSpy).calledWith('igcChange', { detail: 48 }); + expect(spy).toHaveBeenCalledWith('igcInput', { detail: 48 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: 48 }); - eventSpy.resetHistory(); + spy.mockClear(); simulateKeyboard(slider, arrowUp); await elementUpdated(slider); expect(slider.value).to.eq(50); - expect(eventSpy).calledWith('igcInput', { detail: 50 }); - expect(eventSpy).calledWith('igcChange', { detail: 50 }); + expect(spy).toHaveBeenCalledWith('igcInput', { detail: 50 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: 50 }); }); it('fractional step', async () => { - const eventSpy = spy(slider, 'emitEvent'); + const spy = vi.spyOn(slider, 'emitEvent'); const step = 0.25; const lower = 50 - step; @@ -671,22 +669,22 @@ describe('Slider component', () => { await elementUpdated(slider); expect(slider.value).to.equal(lower); - expect(eventSpy).calledWith('igcInput', { detail: lower }); - expect(eventSpy).calledWith('igcChange', { detail: lower }); + expect(spy).toHaveBeenCalledWith('igcInput', { detail: lower }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: lower }); - eventSpy.resetHistory(); + spy.mockClear(); slider.value = 50; simulateKeyboard(slider, arrowRight); await elementUpdated(slider); expect(slider.value).to.equal(higher); - expect(eventSpy).calledWith('igcInput', { detail: higher }); - expect(eventSpy).calledWith('igcChange', { detail: higher }); + expect(spy).toHaveBeenCalledWith('igcInput', { detail: higher }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: higher }); }); it('if step is set to 0 it should default to 1 for keyboard selection', async () => { - const eventSpy = spy(slider, 'emitEvent'); + const spy = vi.spyOn(slider, 'emitEvent'); const value = Math.PI; slider.step = 0; @@ -696,16 +694,16 @@ describe('Slider component', () => { await elementUpdated(slider); expect(slider.value).to.equal(value - 1); - expect(eventSpy).calledWith('igcInput', { detail: value - 1 }); - expect(eventSpy).calledWith('igcChange', { detail: value - 1 }); + expect(spy).toHaveBeenCalledWith('igcInput', { detail: value - 1 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: value - 1 }); - eventSpy.resetHistory(); + spy.mockClear(); slider.value = value; simulateKeyboard(slider, arrowRight); expect(slider.value).to.equal(value + 1); - expect(eventSpy).calledWith('igcInput', { detail: value + 1 }); - expect(eventSpy).calledWith('igcChange', { detail: value + 1 }); + expect(spy).toHaveBeenCalledWith('igcInput', { detail: value + 1 }); + expect(spy).toHaveBeenCalledWith('igcChange', { detail: value + 1 }); }); it('value should be increased/decreased with 1/10th of the slider range when pressing page up/down keys', async () => { @@ -723,7 +721,7 @@ describe('Slider component', () => { }); it('value should be set to minimum when pressing home key', async () => { - const eventSpy = spy(slider, 'emitEvent'); + const spy = vi.spyOn(slider, 'emitEvent'); slider.min = 10; slider.value = 50; @@ -736,11 +734,11 @@ describe('Slider component', () => { expect(slider.value).to.eq(10); // Only one igcInput and one igcChange events should be fired - expect(eventSpy.callCount).to.equal(2); + expect(spy).toHaveBeenCalledTimes(2); }); it('value should be set to maximum when pressing end key', async () => { - const eventSpy = spy(slider, 'emitEvent'); + const spy = vi.spyOn(slider, 'emitEvent'); slider.max = 90; slider.value = 50; @@ -753,14 +751,14 @@ describe('Slider component', () => { expect(slider.value).to.eq(90); // Only one igcInput and one igcChange events should be fired - expect(eventSpy.callCount).to.equal(2); + expect(spy).toHaveBeenCalledTimes(2); }); }); describe('Range', () => { let slider: IgcRangeSliderComponent; - before(() => { + beforeAll(() => { defineComponents(IgcRangeSliderComponent); }); @@ -913,68 +911,68 @@ describe('Slider component', () => { }); it('closest thumb value should be changed when clicking and dragging the slider and corresponding events are fired', async () => { - const eventSpy = spy(slider, 'emitEvent'); + const spy = vi.spyOn(slider, 'emitEvent'); const { x, width } = slider.getBoundingClientRect(); simulatePointerDown(slider, { clientX: x + width * 0.5 }); await elementUpdated(slider); expect(slider.upper).to.eq(50); - expect(eventSpy).calledOnceWithExactly('igcInput', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { detail: { lower: 0, upper: 50 }, }); - eventSpy.resetHistory(); + spy.mockClear(); simulatePointerMove(slider, { clientX: x + width * 0.7 }); await elementUpdated(slider); expect(slider.upper).to.eq(70); - expect(eventSpy).calledOnceWithExactly('igcInput', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { detail: { lower: 0, upper: 70 }, }); - eventSpy.resetHistory(); + spy.mockClear(); simulateLostPointerCapture(slider); await elementUpdated(slider); expect(slider.upper).to.eq(70); - expect(eventSpy).calledOnceWithExactly('igcChange', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { detail: { lower: 0, upper: 70 }, }); - eventSpy.resetHistory(); + spy.mockClear(); simulatePointerDown(slider, { clientX: x + width * 0.2 }); await elementUpdated(slider); expect(slider.lower).to.eq(20); expect(slider.upper).to.eq(70); - expect(eventSpy).calledOnceWithExactly('igcInput', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { detail: { lower: 20, upper: 70 }, }); - eventSpy.resetHistory(); + spy.mockClear(); simulatePointerMove(slider, { clientX: x + width * 0.4 }); await elementUpdated(slider); expect(slider.upper).to.eq(70); expect(slider.lower).to.eq(40); - expect(eventSpy).calledOnceWithExactly('igcInput', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { detail: { lower: 40, upper: 70 }, }); - eventSpy.resetHistory(); + spy.mockClear(); simulateLostPointerCapture(slider); await elementUpdated(slider); expect(slider.upper).to.eq(70); expect(slider.lower).to.eq(40); - expect(eventSpy).calledOnceWithExactly('igcChange', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { detail: { lower: 40, upper: 70 }, }); }); it('when the lower thumb is dragged beyond the upper thumb, the upper thumb should be focused and its dragging should continue.', async () => { - const eventSpy = spy(slider, 'emitEvent'); + const spy = vi.spyOn(slider, 'emitEvent'); const { x, width } = slider.getBoundingClientRect(); const { thumbs } = getDOM(slider); @@ -987,31 +985,31 @@ describe('Slider component', () => { expect(slider.lower).to.eq(25); expect(slider.upper).to.eq(50); - expect(eventSpy).calledOnceWithExactly('igcInput', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { detail: { lower: 25, upper: 50 }, }); expect(slider).to.eq(document.activeElement); expect(thumbs.lower).to.eq(slider.shadowRoot?.activeElement); - eventSpy.resetHistory(); + spy.mockClear(); simulatePointerMove(slider, { clientX: x + width * 0.7 }); await elementUpdated(slider); expect(slider.lower).to.eq(50); expect(slider.upper).to.eq(70); - expect(eventSpy).calledOnceWithExactly('igcInput', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { detail: { lower: 50, upper: 70 }, }); expect(slider).to.eq(document.activeElement); expect(thumbs.upper).to.eq(slider.shadowRoot?.activeElement); - eventSpy.resetHistory(); + spy.mockClear(); simulateLostPointerCapture(slider); await elementUpdated(slider); expect(slider.lower).to.eq(50); expect(slider.upper).to.eq(70); - expect(eventSpy).calledOnceWithExactly('igcChange', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { detail: { lower: 50, upper: 70 }, }); }); @@ -1062,7 +1060,7 @@ describe('Slider component', () => { describe('Initial rendering race condition', () => { let slider: IgcSliderComponent; - before(() => defineComponents(IgcSliderComponent)); + beforeAll(() => defineComponents(IgcSliderComponent)); beforeEach(async () => { slider = await fixture( diff --git a/src/components/snackbar/snackbar.spec.ts b/src/components/snackbar/snackbar.spec.ts index 898563b73..6bbeeb8f1 100644 --- a/src/components/snackbar/snackbar.spec.ts +++ b/src/components/snackbar/snackbar.spec.ts @@ -1,19 +1,17 @@ +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import IgcButtonComponent from '../button/button.js'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, nextFrame, -} from '@open-wc/testing'; -import { type SinonFakeTimers, spy, useFakeTimers } from 'sinon'; - -import IgcButtonComponent from '../button/button.js'; -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { finishAnimationsFor } from '../common/utils.spec.js'; import IgcSnackbarComponent from './snackbar.js'; describe('Snackbar', () => { - before(() => { + beforeAll(() => { defineComponents(IgcSnackbarComponent); }); @@ -21,7 +19,6 @@ describe('Snackbar', () => { const defaultContent = 'Hello world'; let snackbar: IgcSnackbarComponent; - let clock: SinonFakeTimers; describe('DOM', () => { beforeEach(async () => { @@ -110,16 +107,11 @@ describe('Snackbar', () => { }; beforeEach(async () => { - clock = useFakeTimers({ toFake: ['setTimeout'] }); snackbar = await fixture( html`${defaultContent}` ); }); - afterEach(() => { - clock.restore(); - }); - it('`open` property', async () => { checkOpenState(false); @@ -133,16 +125,17 @@ describe('Snackbar', () => { }); it('`displayTime` property', async () => { + vi.useFakeTimers({ toFake: ['setTimeout'] }); snackbar.displayTime = 400; await snackbar.show(); checkOpenState(true); - await clock.tickAsync(399); + await vi.advanceTimersByTimeAsync(399); checkOpenState(true); expect(snackbar.open).to.be.true; // hide timer triggers after this tick - await clock.tickAsync(1); + await vi.advanceTimersByTimeAsync(1); // Stop running animations and repaint finishAnimationsFor(snackbar.shadowRoot!); @@ -150,18 +143,23 @@ describe('Snackbar', () => { expect(snackbar.open).to.be.false; checkOpenState(false); + + vi.useRealTimers(); }); it('`keepOpen` overrides `displayTime`', async () => { + vi.useFakeTimers({ toFake: ['setTimeout'] }); snackbar.displayTime = 200; snackbar.keepOpen = true; await snackbar.show(); checkOpenState(true); - await clock.tickAsync(400); + await vi.advanceTimersByTimeAsync(400); expect(snackbar.open).to.be.true; checkOpenState(true); + + vi.useRealTimers(); }); it('`show()` and `hide()`', async () => { @@ -209,10 +207,10 @@ describe('Snackbar', () => { snackbar.actionText = defaultActionText; await elementUpdated(snackbar); - const eventSpy = spy(snackbar, 'emitEvent'); + const spy = vi.spyOn(snackbar, 'emitEvent'); getDefaultActionButton().click(); - expect(eventSpy).calledOnceWithExactly('igcAction'); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcAction'); }); it('emit `igcAction` with slotted content', async () => { @@ -224,10 +222,10 @@ describe('Snackbar', () => { snackbar.appendChild(button); await elementUpdated(snackbar); - const eventSpy = spy(snackbar, 'emitEvent'); + const spy = vi.spyOn(snackbar, 'emitEvent'); button.click(); - expect(eventSpy).calledOnceWithExactly('igcAction'); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcAction'); }); }); }); diff --git a/src/components/stepper/stepper-utils.spec.ts b/src/components/stepper/stepper-utils.spec.ts index c00b0ec28..2c07f7553 100644 --- a/src/components/stepper/stepper-utils.spec.ts +++ b/src/components/stepper/stepper-utils.spec.ts @@ -1,5 +1,4 @@ -import { fixture, html, unsafeStatic } from '@open-wc/testing'; - +import { fixture, html, unsafeStatic } from '../common/helpers.spec.js'; import type IgcStepComponent from './step.js'; import type IgcStepperComponent from './stepper.js'; diff --git a/src/components/stepper/stepper.spec.ts b/src/components/stepper/stepper.spec.ts index a6cfb6314..3bb872e61 100644 --- a/src/components/stepper/stepper.spec.ts +++ b/src/components/stepper/stepper.spec.ts @@ -1,7 +1,14 @@ -import { elementUpdated, expect } from '@open-wc/testing'; -import { spy } from 'sinon'; - -import { defineComponents } from '../../index.js'; +import { + beforeAll, + beforeEach, + describe, + expect, + it, + type MockInstance, + vi, +} from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated } from '../common/helpers.spec.js'; import IgcStepComponent from './step.js'; import IgcStepperComponent from './stepper.js'; import { @@ -14,12 +21,12 @@ import { } from './stepper-utils.spec.js'; describe('Stepper', () => { - before(() => { + beforeAll(() => { defineComponents(IgcStepperComponent); }); let stepper: IgcStepperComponent; - let eventSpy: any; + let spy: MockInstance; describe('Basic', async () => { beforeEach(async () => { @@ -185,7 +192,7 @@ describe('Stepper', () => { }); it('Should emit ing and ed events when a step is activated through UI', async () => { - eventSpy = spy(stepper, 'emitEvent'); + spy = vi.spyOn(stepper, 'emitEvent'); await elementUpdated(stepper); const argsIng = { @@ -210,13 +217,13 @@ describe('Stepper', () => { stepHeader?.dispatchEvent(new MouseEvent('click')); await elementUpdated(stepper); - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcActiveStepChanging', argsIng); - expect(eventSpy.secondCall).calledWith('igcActiveStepChanged', argsEd); + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcActiveStepChanging', argsIng); + expect(spy).toHaveBeenNthCalledWith(2, 'igcActiveStepChanged', argsEd); }); it('Should not emit events when a step is activated through API', async () => { - eventSpy = spy(stepper, 'emitEvent'); + spy = vi.spyOn(stepper, 'emitEvent'); await elementUpdated(stepper); expect(stepper.steps[0].active).to.be.true; @@ -225,19 +232,19 @@ describe('Stepper', () => { await elementUpdated(stepper); expect(stepper.steps[1].active).to.be.true; - expect(eventSpy.callCount).to.equal(0); + expect(spy).toHaveBeenCalledTimes(0); stepper.prev(); await elementUpdated(stepper); expect(stepper.steps[0].active).to.be.true; - expect(eventSpy.callCount).to.equal(0); + expect(spy).toHaveBeenCalledTimes(0); stepper.navigateTo(2); await elementUpdated(stepper); expect(stepper.steps[2].active).to.be.true; - expect(eventSpy.callCount).to.equal(0); + expect(spy).toHaveBeenCalledTimes(0); }); it('Should be able to cancel the igcActiveStepChanging event', async () => { @@ -1028,7 +1035,7 @@ describe('Stepper', () => { describe('Keyboard navigation', async () => { beforeEach(async () => { stepper = await StepperTestFunctions.createStepperElement(simpleStepper); - eventSpy = spy(stepper, 'emitEvent'); + spy = vi.spyOn(stepper, 'emitEvent'); }); it('Should navigate to the first/last step on Home/End key press', async () => { @@ -1404,7 +1411,7 @@ describe('Stepper', () => { describe('Aria', async () => { beforeEach(async () => { stepper = await StepperTestFunctions.createStepperElement(simpleStepper); - eventSpy = spy(stepper, 'emitEvent'); + spy = vi.spyOn(stepper, 'emitEvent'); }); it('Should render proper role and orientation attributes for the stepper', async () => { diff --git a/src/components/tabs/tabs.spec.ts b/src/components/tabs/tabs.spec.ts index 34acf44a5..af2e8fbb6 100644 --- a/src/components/tabs/tabs.spec.ts +++ b/src/components/tabs/tabs.spec.ts @@ -1,12 +1,5 @@ -import { - elementUpdated, - expect, - fixture, - html, - waitUntil, -} from '@open-wc/testing'; import { range } from 'lit/directives/range.js'; -import { spy } from 'sinon'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import type IgcIconButtonComponent from '../button/icon-button.js'; import { arrowLeft, @@ -17,6 +10,12 @@ import { spaceBar, } from '../common/controllers/key-bindings.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { + elementUpdated, + fixture, + html, + waitUntil, +} from '../common/helpers.spec.js'; import { first, last } from '../common/util.js'; import { simulateClick, simulateKeyboard } from '../common/utils.spec.js'; import IgcTabComponent from './tab.js'; @@ -33,7 +32,7 @@ describe('Tabs component', () => { expect(getComputedStyle(getTabDOM(tab).body).display).to.equal('block'); } - before(() => { + beforeAll(() => { defineComponents(IgcTabComponent, IgcTabsComponent); }); @@ -288,55 +287,55 @@ describe('Tabs component', () => { activeTabWidth = activeTabHeader.getBoundingClientRect().width; expect(indicator.style.transform).to.eq( - `translateX(${activeTabOffsetLeft - scrollContainerWidth + activeTabWidth}px)` + `translateX(${Math.round(activeTabOffsetLeft - scrollContainerWidth + activeTabWidth)}px)` ); - expect(indicator.style.width).to.eq(`${activeTabWidth}px`); + expect(indicator.style.width).to.eq(`${Math.round(activeTabWidth)}px`); }); it('emits `igcChange` when selecting item via mouse click', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateClick(getTabDOM(element.tabs[3]).header); await elementUpdated(element); - expect(eventSpy).calledWithExactly('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: first(getTabsDOM(element).selected), }); }); it('emits `igcChange` when selecting item via arrow key press', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateKeyboard(getTabDOM(element.tabs[1]).header, arrowLeft); await elementUpdated(element); - expect(eventSpy).calledWithExactly('igcChange', { + expect(spy).toHaveBeenCalledWith('igcChange', { detail: first(getTabsDOM(element).selected), }); }); it('does not change active tab when clicking inside the tab content', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const input = document.createElement('input'); element.tabs[1].append(input); simulateClick(input); await elementUpdated(element); - expect(eventSpy.callCount).to.equal(0); + expect(spy).toHaveBeenCalledTimes(0); verifySelection(element, element.tabs[1]); }); it('does not change active tab with keyboard interaction inside the tab content', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); const input = document.createElement('input'); element.tabs[1].append(input); simulateKeyboard(input, arrowLeft); await elementUpdated(element); - expect(eventSpy.callCount).to.equal(0); + expect(spy).toHaveBeenCalledTimes(0); verifySelection(element, element.tabs[1]); }); @@ -352,7 +351,7 @@ describe('Tabs component', () => { .map((tab) => getTabDOM(tab).header.offsetWidth - expectedWidth) .reduce((a, b) => a - b, 0); - expect(diff).to.equal(0); + expect(diff).to.approximately(0, 2); }); it('aligns tab headers properly when `alignment` is set to start', async () => { diff --git a/src/components/textarea/textarea.spec.ts b/src/components/textarea/textarea.spec.ts index 983575a0c..d866622dd 100644 --- a/src/components/textarea/textarea.spec.ts +++ b/src/components/textarea/textarea.spec.ts @@ -1,14 +1,13 @@ +import type { TemplateResult } from 'lit'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { configureTheme } from '../../theming/config.js'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, nextFrame, -} from '@open-wc/testing'; -import type { TemplateResult } from 'lit'; -import { spy } from 'sinon'; -import { configureTheme } from '../../theming/config.js'; -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { createFormAssociatedTestBed, isFocused, @@ -21,7 +20,7 @@ import { import IgcTextareaComponent from './textarea.js'; describe('Textarea component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcTextareaComponent); }); @@ -172,22 +171,24 @@ describe('Textarea component', () => { }); it('igcInput', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); simulateInput(textArea, { value: '123' }); await elementUpdated(element); - expect(eventSpy).calledOnceWithExactly('igcInput', { detail: '123' }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcInput', { + detail: '123', + }); expect(element.value).to.equal(textArea.value); }); it('igcChange', async () => { - const eventSpy = spy(element, 'emitEvent'); + const spy = vi.spyOn(element, 'emitEvent'); textArea.value = '20230317'; textArea.dispatchEvent(new Event('change')); - expect(eventSpy).calledOnceWithExactly('igcChange', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcChange', { detail: '20230317', }); expect(element.value).to.equal(textArea.value); diff --git a/src/components/tile-manager/tile-dnd.spec.ts b/src/components/tile-manager/tile-dnd.spec.ts index 5adfb42eb..b2d43ae5a 100644 --- a/src/components/tile-manager/tile-dnd.spec.ts +++ b/src/components/tile-manager/tile-dnd.spec.ts @@ -1,15 +1,14 @@ +import { range } from 'lit/directives/range.js'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import IgcIconButtonComponent from '../button/icon-button.js'; +import { escapeKey } from '../common/controllers/key-bindings.js'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, nextFrame, -} from '@open-wc/testing'; -import { range } from 'lit/directives/range.js'; -import { restore, spy, stub } from 'sinon'; -import IgcIconButtonComponent from '../button/icon-button.js'; -import { escapeKey } from '../common/controllers/key-bindings.js'; -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { getCenterPoint } from '../common/util.js'; import { simulateClick, @@ -23,7 +22,7 @@ import IgcTileComponent from './tile.js'; import IgcTileManagerComponent from './tile-manager.js'; describe('Tile drag and drop', () => { - before(() => { + beforeAll(() => { defineComponents(IgcTileManagerComponent); }); @@ -79,6 +78,10 @@ describe('Tile drag and drop', () => { } async function dragAndDrop(tile: IgcTileComponent, target: IgcTileComponent) { + // Ensure target is visible in viewport for elementsFromPoint to work + target.scrollIntoView({ block: 'center' }); + await nextFrame(); + const { x, y } = getCenterPoint(target); simulatePointerDown(tile); @@ -116,11 +119,11 @@ describe('Tile drag and drop', () => { const draggedTile = getTile(0); const dropTarget = getTile(1); - const eventSpy = spy(draggedTile, 'emitEvent'); + const spy = vi.spyOn(draggedTile, 'emitEvent'); await dragAndDrop(draggedTile, dropTarget); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(draggedTile.position).to.equal(0); expect(dropTarget.position).to.equal(1); }); @@ -158,12 +161,12 @@ describe('Tile drag and drop', () => { it('should correctly fire `igcTileDragStart` event', async () => { const tile = getTile(0); - const eventSpy = spy(tile, 'emitEvent'); + const spy = vi.spyOn(tile, 'emitEvent'); simulatePointerDown(tile); await elementUpdated(tile); - expect(eventSpy).calledOnceWithExactly('igcTileDragStart', { + expect(spy).toHaveBeenCalledWith('igcTileDragStart', { detail: tile, cancelable: true, }); @@ -171,7 +174,7 @@ describe('Tile drag and drop', () => { it('should stop drag operation when `igcTileDragStart` is prevented', async () => { const [tile, target] = [getTile(0), getTile(4)]; - const eventSpy = spy(tile, 'emitEvent'); + const spy = vi.spyOn(tile, 'emitEvent'); tile.addEventListener('igcTileDragStart', (event) => { event.preventDefault(); @@ -179,19 +182,22 @@ describe('Tile drag and drop', () => { await dragAndDrop(tile, target); - expect(eventSpy).calledOnceWith('igcTileDragStart'); - expect(eventSpy).not.calledWith('igcTileDragEnd'); - expect(eventSpy.callCount).to.equal(1); + expect(spy).toHaveBeenCalledWith('igcTileDragStart', { + detail: tile, + cancelable: true, + }); + expect(spy).not.toHaveBeenCalledWith('igcTileDragEnd'); + expect(spy).toHaveBeenCalledTimes(1); }); it('should correctly fire `igcTileDragEnd` event', async () => { const [tile, target] = [getTile(0), getTile(4)]; - const eventSpy = spy(tile, 'emitEvent'); + const spy = vi.spyOn(tile, 'emitEvent'); await dragAndDrop(tile, target); - expect(eventSpy).calledTwice; - expect(eventSpy).calledWith('igcTileDragEnd', { + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenCalledWith('igcTileDragEnd', { detail: tile, }); }); @@ -199,23 +205,34 @@ describe('Tile drag and drop', () => { it('should cancel dragging with Escape', async () => { const draggedTile = getTile(0); const dropTarget = getTile(4); - const eventSpy = spy(draggedTile, 'emitEvent'); + const spy = vi.spyOn(draggedTile, 'emitEvent'); + + // Scroll the target into view before getting coordinates + dropTarget.scrollIntoView({ block: 'center' }); + await nextFrame(); + const { x, y } = getCenterPoint(dropTarget); expect(draggedTile.position).to.equal(0); expect(dropTarget.position).to.equal(4); simulatePointerDown(draggedTile); - simulateTileDragOver(draggedTile, { x, y }); + await elementUpdated(draggedTile); + simulateTileDragOver(draggedTile, { x, y }); + await elementUpdated(tileManager); await viewTransitionComplete(); + expect(draggedTile.position).to.equal(4); expect(dropTarget.position).to.equal(0); simulateKeyboard(tileManager, escapeKey); + await elementUpdated(tileManager); await viewTransitionComplete(); - expect(eventSpy).calledWith('igcTileDragCancel'); + expect(spy).toHaveBeenCalledWith('igcTileDragCancel', { + detail: draggedTile, + }); expect(draggedTile.position).to.equal(0); expect(dropTarget.position).to.equal(4); @@ -273,6 +290,10 @@ describe('Tile drag and drop', () => { dropTarget.colSpan = 3; await elementUpdated(tileManager); + // Ensure target is visible in viewport for elementsFromPoint to work + dropTarget.scrollIntoView({ block: 'center' }); + await nextFrame(); + const { x, y } = getCenterPoint(dropTarget); simulatePointerDown(draggedTile); @@ -350,6 +371,9 @@ describe('Tile drag and drop', () => { const draggedTile = getTile(0); const dropTarget = getTile(1); + // Ensure enough columns for the explicit positioning + tileManager.columnCount = 4; + draggedTile.colStart = 2; draggedTile.rowStart = 2; @@ -399,7 +423,7 @@ describe('Tile drag and drop', () => { it('should rearrange tiles when the tile is dropped', async () => { const draggedTile = getTile(3); const dropTarget = getTile(1); - const eventSpy = spy(draggedTile, 'emitEvent'); + const spy = vi.spyOn(draggedTile, 'emitEvent'); await dragAndDropFromHeader(draggedTile, dropTarget); @@ -411,7 +435,7 @@ describe('Tile drag and drop', () => { 'tile4', ]; - expect(eventSpy).calledTwice; + expect(spy).toHaveBeenCalledTimes(2); tileManager.tiles.forEach((tile, index) => { expect(tile.id).to.equal(expectedIdsAfterDrag[index]); }); @@ -422,7 +446,7 @@ describe('Tile drag and drop', () => { it('should not start dragging if pointer is not over the header', async () => { const draggedTile = getTile(0); const dropTarget = getTile(1); - const eventSpy = spy(draggedTile, 'emitEvent'); + const spy = vi.spyOn(draggedTile, 'emitEvent'); const contentContainer = getTileContentContainer(draggedTile); const { x, y } = getCenterPoint(dropTarget); @@ -435,7 +459,7 @@ describe('Tile drag and drop', () => { simulateLostPointerCapture(draggedTile); await elementUpdated(tileManager); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(draggedTile.position).to.equal(0); expect(dropTarget.position).to.equal(1); }); @@ -462,14 +486,14 @@ describe('Tile drag and drop', () => { it('should disable drag and drop when tile is maximized', async () => { const draggedTile = getTile(0); const dropTarget = getTile(1); - const eventSpy = spy(draggedTile, 'emitEvent'); + const spy = vi.spyOn(draggedTile, 'emitEvent'); draggedTile.maximized = true; await elementUpdated(tileManager); await dragAndDrop(draggedTile, dropTarget); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); expect(draggedTile.position).to.equal(0); expect(dropTarget.position).to.equal(1); }); @@ -477,13 +501,13 @@ describe('Tile drag and drop', () => { it('should disable drag and drop when tile is in fullscreen mode', async () => { const draggedTile = getTile(0); const dropTarget = getTile(1); - const eventSpy = spy(draggedTile, 'emitEvent'); + const spy = vi.spyOn(draggedTile, 'emitEvent'); const buttonFullscreen = draggedTile.renderRoot.querySelector( '[name="fullscreen"]' )!; - draggedTile.requestFullscreen = stub().callsFake(() => { + draggedTile.requestFullscreen = vi.fn().mockImplementation(() => { Object.defineProperty(document, 'fullscreenElement', { value: draggedTile, configurable: true, @@ -498,8 +522,8 @@ describe('Tile drag and drop', () => { await dragAndDrop(draggedTile, dropTarget); - expect(eventSpy).not.calledWith('igcTileDragStart'); - expect(eventSpy).not.calledWith('igcTileDragEnd'); + expect(spy).not.toHaveBeenCalledWith('igcTileDragStart'); + expect(spy).not.toHaveBeenCalledWith('igcTileDragEnd'); const expectedIdsAfterDrag = [ 'tile0', @@ -519,21 +543,21 @@ describe('Tile drag and drop', () => { configurable: true, }); - restore(); + vi.restoreAllMocks(); }); it('should not start a drag operation when interacting with the default tile actions in `tile-header` drag mode', async () => { tileManager.dragMode = 'tile-header'; const tile = getTile(0); const [maximize, _] = getActionButtons(tile); - const eventSpy = spy(tile, 'emitEvent'); + const spy = vi.spyOn(tile, 'emitEvent'); // Wait for maximized transition trigger from UI maximize.click(); await viewTransitionComplete(); expect(tile.maximized).to.be.true; - expect(eventSpy).not.calledWith('igcTileDragStart'); + expect(spy).not.toHaveBeenCalledWith('igcTileDragStart'); // Wait for maximized transition trigger from UI maximize.click(); @@ -546,14 +570,14 @@ describe('Tile drag and drop', () => { tileManager.dragMode = 'tile'; const tile = getTile(0); const [maximize, _] = getActionButtons(tile); - const eventSpy = spy(tile, 'emitEvent'); + const spy = vi.spyOn(tile, 'emitEvent'); // Wait for maximized transition trigger from UI maximize.click(); await viewTransitionComplete(); expect(tile.maximized).to.be.true; - expect(eventSpy).not.calledWith('igcTileDragStart'); + expect(spy).not.toHaveBeenCalledWith('igcTileDragStart'); // Wait for maximized transition trigger from UI maximize.click(); @@ -564,7 +588,7 @@ describe('Tile drag and drop', () => { it('should not start a drag operation when interacting with the slotted tile actions in `tile-header` drag mode', async () => { const { tile, button } = createSlottedActionTile(); - const eventSpy = spy(tile, 'emitEvent'); + const spy = vi.spyOn(tile, 'emitEvent'); tileManager.dragMode = 'tile-header'; tileManager.append(tile); @@ -573,12 +597,12 @@ describe('Tile drag and drop', () => { button.click(); await elementUpdated(tile); - expect(eventSpy).not.calledWith('igcTileDragStart'); + expect(spy).not.toHaveBeenCalledWith('igcTileDragStart'); }); it('should not start a drag operation when interacting with the slotted tile actions in `tile` drag mode', async () => { const { tile, button } = createSlottedActionTile(); - const eventSpy = spy(tile, 'emitEvent'); + const spy = vi.spyOn(tile, 'emitEvent'); tileManager.dragMode = 'tile'; tileManager.append(tile); @@ -587,7 +611,7 @@ describe('Tile drag and drop', () => { button.click(); await elementUpdated(tile); - expect(eventSpy).not.calledWith('igcTileDragStart'); + expect(spy).not.toHaveBeenCalledWith('igcTileDragStart'); }); }); }); diff --git a/src/components/tile-manager/tile-manager.spec.ts b/src/components/tile-manager/tile-manager.spec.ts index e9ac95cdc..2612ed667 100644 --- a/src/components/tile-manager/tile-manager.spec.ts +++ b/src/components/tile-manager/tile-manager.spec.ts @@ -1,21 +1,28 @@ +import { range } from 'lit/directives/range.js'; import { - elementUpdated, + afterEach, + beforeAll, + beforeEach, + describe, expect, + it, + vi, +} from 'vitest'; +import IgcIconButtonComponent from '../button/icon-button.js'; +import { defineComponents } from '../common/definitions/defineComponents.js'; +import { + elementUpdated, fixture, html, nextFrame, -} from '@open-wc/testing'; -import { range } from 'lit/directives/range.js'; -import { match, restore, spy, stub } from 'sinon'; -import IgcIconButtonComponent from '../button/icon-button.js'; -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { first } from '../common/util.js'; import { simulateClick } from '../common/utils.spec.js'; import IgcTileComponent from './tile.js'; import IgcTileManagerComponent from './tile-manager.js'; describe('Tile Manager component', () => { - before(() => { + beforeAll(() => { defineComponents(IgcTileManagerComponent); }); @@ -213,9 +220,7 @@ describe('Tile Manager component', () => { it("should check tile manager's row and column template style props", async () => { const style = getComputedStyle(getTileManagerBase()); - expect(style.gridTemplateColumns).to.equal( - '234.656px 234.656px 234.656px 0px 0px' - ); + expect(style.gridTemplateColumns).to.equal('334px 0px 0px 0px 0px'); tileManager.columnCount = 15; await elementUpdated(tileManager); @@ -497,13 +502,14 @@ describe('Tile Manager component', () => { describe('Tile state change behavior', () => { let tile: any; + let exitFullscreenMock: ReturnType; beforeEach(async () => { tileManager = await fixture(createTileManager()); tile = first(tileManager.tiles); // Mock `requestFullscreen` - tile.requestFullscreen = stub().callsFake(() => { + tile.requestFullscreen = vi.fn().mockImplementation(() => { Object.defineProperty(document, 'fullscreenElement', { value: tile, configurable: true, @@ -512,14 +518,15 @@ describe('Tile Manager component', () => { }); // Mock `exitFullscreen` + exitFullscreenMock = vi.fn().mockImplementation(() => { + Object.defineProperty(document, 'fullscreenElement', { + value: null, + configurable: true, + }); + return Promise.resolve(); + }); Object.defineProperty(document, 'exitFullscreen', { - value: stub().callsFake(() => { - Object.defineProperty(document, 'fullscreenElement', { - value: null, - configurable: true, - }); - return Promise.resolve(); - }), + value: exitFullscreenMock, configurable: true, }); }); @@ -530,7 +537,7 @@ describe('Tile Manager component', () => { configurable: true, }); - restore(); + vi.restoreAllMocks(); }); it('should correctly change fullscreen state on button click', async () => { @@ -538,25 +545,25 @@ describe('Tile Manager component', () => { simulateClick(btnFullscreen); await elementUpdated(tileManager); - expect(tile.requestFullscreen).to.have.been.calledOnce; - expect(document.exitFullscreen).to.not.have.been.called; + expect(tile.requestFullscreen).toHaveBeenCalledOnce(); + expect(exitFullscreenMock).not.toHaveBeenCalled(); expect(tile.fullscreen).to.be.true; simulateClick(btnFullscreen); await elementUpdated(tileManager); - expect(document.exitFullscreen).to.have.been.calledOnce; + expect(exitFullscreenMock).toHaveBeenCalledOnce(); expect(tile.fullscreen).to.be.false; }); it('should correctly fire `igcTileFullscreen` event', async () => { - const eventSpy = spy(tile, 'emitEvent'); + const eventSpy = vi.spyOn(tile, 'emitEvent'); const fullscreenButton = getActionButtons(tile)[1]; simulateClick(fullscreenButton!); await elementUpdated(tileManager); - expect(eventSpy).calledWith('igcTileFullscreen', { + expect(eventSpy).toHaveBeenCalledWith('igcTileFullscreen', { detail: { tile: tile, state: true }, cancelable: true, }); @@ -565,7 +572,7 @@ describe('Tile Manager component', () => { simulateClick(fullscreenButton!); await elementUpdated(tileManager); - expect(eventSpy).calledWith('igcTileFullscreen', { + expect(eventSpy).toHaveBeenCalledWith('igcTileFullscreen', { detail: { tile: tile, state: false }, cancelable: true, }); @@ -573,7 +580,7 @@ describe('Tile Manager component', () => { }); it('can cancel `igcTileFullscreen` event', async () => { - const eventSpy = spy(tile, 'emitEvent'); + const eventSpy = vi.spyOn(tile, 'emitEvent'); const fullscreenButton = getActionButtons(tile)[1]; tile.addEventListener('igcTileFullscreen', (ev: CustomEvent) => { @@ -583,15 +590,15 @@ describe('Tile Manager component', () => { simulateClick(fullscreenButton!); await elementUpdated(tileManager); - expect(eventSpy).calledWith( + expect(eventSpy).toHaveBeenCalledWith( 'igcTileFullscreen', - match({ + expect.objectContaining({ detail: { tile: tile, state: true }, cancelable: true, }) ); expect(tile.fullscreen).to.be.false; - expect(tile.requestFullscreen).not.to.have.been.called; + expect(tile.requestFullscreen).not.toHaveBeenCalled(); }); it('should update fullscreen property on fullscreenchange (e.g. Esc key is pressed)', async () => { @@ -643,14 +650,14 @@ describe('Tile Manager component', () => { }); it('should correctly fire `igcTileMaximize` event on clicking Maximize button', async () => { - const eventSpy = spy(tile, 'emitEvent'); + const eventSpy = vi.spyOn(tile, 'emitEvent'); const btnMaximize = getActionButtons(tile)[0]; // Wait for maximized transition trigger from UI simulateClick(btnMaximize); await viewTransitionComplete(); - expect(eventSpy).calledWith('igcTileMaximize', { + expect(eventSpy).toHaveBeenCalledWith('igcTileMaximize', { detail: { tile: tile, state: true }, cancelable: true, }); @@ -660,8 +667,8 @@ describe('Tile Manager component', () => { simulateClick(btnMaximize); await viewTransitionComplete(); - expect(eventSpy).to.have.been.calledTwice; - expect(eventSpy).calledWith('igcTileMaximize', { + expect(eventSpy).toHaveBeenCalledTimes(2); + expect(eventSpy).toHaveBeenCalledWith('igcTileMaximize', { detail: { tile: tile, state: false }, cancelable: true, }); @@ -669,7 +676,7 @@ describe('Tile Manager component', () => { }); it('can cancel `igcTileMaximize` event', async () => { - const eventSpy = spy(tile, 'emitEvent'); + const eventSpy = vi.spyOn(tile, 'emitEvent'); tile.addEventListener('igcTileMaximize', (event: Event) => { event.preventDefault(); @@ -679,7 +686,8 @@ describe('Tile Manager component', () => { simulateClick(btnMaximize); await elementUpdated(tileManager); - expect(eventSpy).calledOnceWithExactly('igcTileMaximize', { + expect(eventSpy).toHaveBeenCalledOnce(); + expect(eventSpy).toHaveBeenCalledWith('igcTileMaximize', { detail: { tile: tile, state: true }, cancelable: true, }); diff --git a/src/components/tile-manager/tile-resize.spec.ts b/src/components/tile-manager/tile-resize.spec.ts index eaea3fb24..7e6905d77 100644 --- a/src/components/tile-manager/tile-resize.spec.ts +++ b/src/components/tile-manager/tile-resize.spec.ts @@ -1,31 +1,29 @@ +import { range } from 'lit/directives/range.js'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { escapeKey } from '../common/controllers/key-bindings.js'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { elementUpdated, - expect, fixture, html, nextFrame, -} from '@open-wc/testing'; -import { range } from 'lit/directives/range.js'; -import type Sinon from 'sinon'; -import { spy } from 'sinon'; -import { escapeKey } from '../common/controllers/key-bindings.js'; -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { first, last } from '../common/util.js'; import { + expectCalledWith, + expectNotCalledWith, simulateKeyboard, simulateLostPointerCapture, simulatePointerDown, simulatePointerMove, } from '../common/utils.spec.js'; -import IgcResizeContainerComponent, { - type IgcResizeContainerComponentEventMap, -} from '../resize-container/resize-container.js'; +import IgcResizeContainerComponent from '../resize-container/resize-container.js'; import type { ResizeCallbackParams } from '../resize-container/types.js'; import IgcTileComponent from './tile.js'; import IgcTileManagerComponent from './tile-manager.js'; describe('Tile resize', () => { - before(() => { + beforeAll(() => { defineComponents(IgcTileManagerComponent); }); @@ -145,7 +143,7 @@ describe('Tile resize', () => { it('should create a ghost element on resize start', async () => { const DOM = getResizeContainerDOM(firstTile); - const eventSpy = spy(DOM.resizeElement, 'emitEvent'); + const eventSpy = vi.spyOn(DOM.resizeElement, 'emitEvent'); expect(DOM.ghostElement).to.be.null; @@ -153,19 +151,19 @@ describe('Tile resize', () => { await elementUpdated(DOM.resizeElement); expect(DOM.ghostElement).to.not.be.null; - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(eventSpy, 'igcResizeStart'); }); it('should update ghost element styles during pointer move', async () => { const DOM = getResizeContainerDOM(firstTile); - const eventSpy = spy(DOM.resizeElement, 'emitEvent'); + const eventSpy = vi.spyOn(DOM.resizeElement, 'emitEvent'); const tileRect = firstTile.getBoundingClientRect(); simulatePointerDown(DOM.adorners.corner); await elementUpdated(DOM.resizeElement); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(eventSpy, 'igcResizeStart'); expect(DOM.ghostElement.getBoundingClientRect()).to.eql(tileRect); simulatePointerMove(DOM.adorners.corner, { @@ -176,7 +174,7 @@ describe('Tile resize', () => { const state = getResizeEventState(eventSpy); - expect(eventSpy).calledWith('igcResize'); + expectCalledWith(eventSpy, 'igcResize'); expect(state.ghost).to.equal(DOM.ghostElement); expect(state.initial).to.eql(tileRect); assertRectsAreEqual( @@ -187,12 +185,12 @@ describe('Tile resize', () => { it('Should correctly resize column with auto grid', async () => { const DOM = getResizeContainerDOM(firstTile); - const eventSpy = spy(DOM.resizeElement, 'emitEvent'); + const eventSpy = vi.spyOn(DOM.resizeElement, 'emitEvent'); simulatePointerDown(DOM.adorners.side); await elementUpdated(DOM.container); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(eventSpy, 'igcResizeStart'); expect(getComputedStyle(firstTile).gridColumn).to.eql('auto / span 1'); simulatePointerMove(DOM.adorners.side, { @@ -201,13 +199,13 @@ describe('Tile resize', () => { await elementUpdated(DOM.resizeElement); - expect(eventSpy).calledWith('igcResize'); + expectCalledWith(eventSpy, 'igcResize'); simulateLostPointerCapture(DOM.adorners.side); await elementUpdated(DOM.resizeElement); await nextFrame(); - expect(eventSpy).calledWith('igcResizeEnd'); + expectCalledWith(eventSpy, 'igcResizeEnd'); expect(DOM.ghostElement).to.be.null; expect(getComputedStyle(firstTile).gridColumn).to.eql('auto / span 2'); @@ -406,19 +404,19 @@ describe('Tile resize', () => { await elementUpdated(DOM.resizeElement); await nextFrame(); - expect(getComputedStyle(firstTile).gridRow).to.eql('2 / span 2'); + expect(getComputedStyle(firstTile).gridRow).to.eql('2 / span 3'); }); it('should cancel resize by pressing ESC key', async () => { const DOM = getResizeContainerDOM(firstTile); - const eventSpy = spy(DOM.resizeElement, 'emitEvent'); + const eventSpy = vi.spyOn(DOM.resizeElement, 'emitEvent'); const tileRect = firstTile.getBoundingClientRect(); simulatePointerDown(DOM.adorners.corner); await elementUpdated(DOM.resizeElement); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(eventSpy, 'igcResizeStart'); simulatePointerMove(DOM.adorners.corner, { clientX: tileRect.right * 2, @@ -431,24 +429,24 @@ describe('Tile resize', () => { simulateKeyboard(DOM.resizeElement, escapeKey); await elementUpdated(DOM.resizeElement); - expect(eventSpy).calledWith('igcResizeCancel'); + expectCalledWith(eventSpy, 'igcResizeCancel'); expect(DOM.ghostElement).to.be.null; assertRectsAreEqual(firstTile.getBoundingClientRect(), tileRect); }); it('should fire `igcTileResizeStart` when a resize operation begins', async () => { const DOM = getResizeContainerDOM(firstTile); - const eventSpy = spy(firstTile, 'emitEvent'); + const eventSpy = vi.spyOn(firstTile, 'emitEvent'); simulatePointerDown(DOM.adorners.corner); await elementUpdated(firstTile); - expect(eventSpy).calledWith('igcTileResizeStart'); + expectCalledWith(eventSpy, 'igcTileResizeStart'); }); it('should stop resize operations by canceling the `igcTileResizeStart` event', async () => { const DOM = getResizeContainerDOM(firstTile); - const eventSpy = spy(firstTile, 'emitEvent'); + const eventSpy = vi.spyOn(firstTile, 'emitEvent'); firstTile.addEventListener('igcTileResizeStart', (event) => event.preventDefault() @@ -457,19 +455,19 @@ describe('Tile resize', () => { simulatePointerDown(DOM.adorners.corner); await elementUpdated(firstTile); - expect(eventSpy).calledWith('igcTileResizeStart'); + expectCalledWith(eventSpy, 'igcTileResizeStart'); simulatePointerMove(DOM.adorners.corner); simulateLostPointerCapture(DOM.adorners.corner); await elementUpdated(firstTile); - expect(eventSpy.callCount).to.equal(1); - expect(eventSpy).not.calledWith('igcTileResizeEnd'); + expect(eventSpy).toHaveBeenCalledTimes(1); + expectNotCalledWith(eventSpy, 'igcTileResizeEnd'); }); it('should fire `igcTileResizeEnd` when a resize operation is performed successfully', async () => { const DOM = getResizeContainerDOM(firstTile); - const eventSpy = spy(firstTile, 'emitEvent'); + const eventSpy = vi.spyOn(firstTile, 'emitEvent'); const { colSpan: initialColumnSpan, rowSpan: initialRowSpan } = firstTile; @@ -478,7 +476,7 @@ describe('Tile resize', () => { simulatePointerDown(DOM.adorners.corner); await elementUpdated(firstTile); - expect(eventSpy).calledWith('igcTileResizeStart'); + expectCalledWith(eventSpy, 'igcTileResizeStart'); simulatePointerMove(DOM.adorners.corner, { clientX: tileRect.right * 2, @@ -487,7 +485,7 @@ describe('Tile resize', () => { simulateLostPointerCapture(DOM.adorners.corner); await viewTransitionComplete(); - expect(eventSpy).calledWith('igcTileResizeEnd'); + expectCalledWith(eventSpy, 'igcTileResizeEnd'); expect(DOM.ghostElement).to.be.null; const { colSpan, rowSpan } = firstTile; @@ -497,14 +495,14 @@ describe('Tile resize', () => { it('should fire `igcTileResizeCancel` when canceling a resize operation', async () => { const DOM = getResizeContainerDOM(firstTile); - const eventSpy = spy(firstTile, 'emitEvent'); + const eventSpy = vi.spyOn(firstTile, 'emitEvent'); const tileRect = firstTile.getBoundingClientRect(); simulatePointerDown(DOM.adorners.corner); await elementUpdated(firstTile); - expect(eventSpy).calledWith('igcTileResizeStart'); + expectCalledWith(eventSpy, 'igcTileResizeStart'); simulatePointerMove(DOM.adorners.corner, { clientX: tileRect.right * 2, @@ -517,7 +515,7 @@ describe('Tile resize', () => { simulateKeyboard(DOM.resizeElement, escapeKey); await elementUpdated(firstTile); - expect(eventSpy).calledWith('igcTileResizeCancel'); + expectCalledWith(eventSpy, 'igcTileResizeCancel'); expect(DOM.ghostElement).to.be.null; assertRectsAreEqual(firstTile.getBoundingClientRect(), tileRect); }); @@ -535,14 +533,14 @@ describe('Tile resize', () => { it('should update tile parts on resizing', async () => { const DOM = getResizeContainerDOM(firstTile); - const eventSpy = spy(DOM.resizeElement, 'emitEvent'); + const eventSpy = vi.spyOn(DOM.resizeElement, 'emitEvent'); expect(firstTile.part.length).to.equal(0); simulatePointerDown(DOM.adorners.bottom); await elementUpdated(DOM.resizeElement); - expect(eventSpy).calledWith('igcResizeStart'); + expectCalledWith(eventSpy, 'igcResizeStart'); expect(firstTile.part.length).to.be.greaterThan(0); expect(firstTile.part.contains('resizing')).to.be.true; }); @@ -591,15 +589,14 @@ function getResizeContainerDOM(tile: IgcTileComponent) { } function getResizeEventState( - eventSpy: Sinon.SinonSpy< - [ - type: keyof IgcResizeContainerComponentEventMap, - eventInitDict?: CustomEventInit | undefined, - ], - boolean - > + eventSpy: ReturnType ): ResizeCallbackParams['state'] { - return eventSpy.lastCall.lastArg.detail.state; + const calls = eventSpy.mock.calls; + const lastCall = calls[calls.length - 1] as [ + string, + CustomEventInit<{ state: ResizeCallbackParams['state'] }>, + ]; + return lastCall[1]!.detail!.state; } function assertRectsAreEqual(a: DOMRect, b: DOMRect, delta = 0.01) { diff --git a/src/components/toast/toast.spec.ts b/src/components/toast/toast.spec.ts index 0b00bd35e..5519e9374 100644 --- a/src/components/toast/toast.spec.ts +++ b/src/components/toast/toast.spec.ts @@ -1,21 +1,26 @@ import { - elementUpdated, + afterEach, + beforeAll, + beforeEach, + describe, expect, + it, + vi, +} from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; +import { + elementUpdated, fixture, html, nextFrame, -} from '@open-wc/testing'; - -import { type SinonFakeTimers, useFakeTimers } from 'sinon'; -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { finishAnimationsFor } from '../common/utils.spec.js'; import IgcToastComponent from './toast.js'; describe('Toast', () => { - before(() => defineComponents(IgcToastComponent)); + beforeAll(() => defineComponents(IgcToastComponent)); let toast: IgcToastComponent; - let clock: SinonFakeTimers; describe('ARIA', () => { beforeEach(async () => { @@ -32,14 +37,14 @@ describe('Toast', () => { describe('API', () => { beforeEach(async () => { - clock = useFakeTimers({ toFake: ['setTimeout'] }); + vi.useFakeTimers({ toFake: ['setTimeout'] }); toast = await fixture( html`Hello world` ); }); afterEach(() => { - clock.restore(); + vi.useRealTimers(); }); const checkOpenState = (state = false) => { @@ -69,11 +74,11 @@ describe('Toast', () => { await toast.show(); checkOpenState(true); - await clock.tickAsync(399); + await vi.advanceTimersByTimeAsync(399); expect(toast.open).to.be.true; checkOpenState(true); - await clock.tickAsync(1); + await vi.advanceTimersByTimeAsync(1); finishAnimationsFor(toast); await nextFrame(); @@ -88,7 +93,7 @@ describe('Toast', () => { await toast.show(); checkOpenState(true); - await clock.tickAsync(400); + await vi.advanceTimersByTimeAsync(400); expect(toast.open).to.be.true; checkOpenState(true); }); diff --git a/src/components/tooltip/tooltip.spec.ts b/src/components/tooltip/tooltip.spec.ts index e9d086c7d..0cc1a85ac 100644 --- a/src/components/tooltip/tooltip.spec.ts +++ b/src/components/tooltip/tooltip.spec.ts @@ -1,12 +1,20 @@ import { - elementUpdated, + afterEach, + beforeAll, + beforeEach, + describe, expect, + it, + type MockInstance, + vi, +} from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; +import { + elementUpdated, fixture, html, nextFrame, -} from '@open-wc/testing'; -import { type SinonFakeTimers, spy, useFakeTimers } from 'sinon'; -import { defineComponents } from '../common/definitions/defineComponents.js'; +} from '../common/helpers.spec.js'; import { finishAnimationsFor, simulateBlur, @@ -20,7 +28,7 @@ import IgcTooltipComponent from './tooltip.js'; describe('Tooltip', () => { let anchor: HTMLButtonElement; let tooltip: IgcTooltipComponent; - let clock: SinonFakeTimers; + let clock: ReturnType; const DIFF_OPTIONS = { ignoreAttributes: ['anchor'], @@ -31,7 +39,7 @@ describe('Tooltip', () => { const DEFAULT_SHOW_DELAY = 200; const DEFAULT_HIDE_DELAY = 300; - before(() => { + beforeAll(() => { defineComponents(IgcTooltipComponent); }); @@ -139,14 +147,14 @@ describe('Tooltip', () => { describe('Properties Tests', () => { beforeEach(async () => { - clock = useFakeTimers({ toFake: ['setTimeout'] }); + clock = vi.useFakeTimers({ toFake: ['setTimeout'] }); const container = await fixture(createTooltipWithTarget()); tooltip = container.querySelector(IgcTooltipComponent.tagName)!; anchor = container.querySelector('button')!; }); afterEach(() => { - clock.restore(); + clock.useRealTimers(); }); it('should set target via `anchor` property', async () => { @@ -168,12 +176,12 @@ describe('Tooltip', () => { // If no anchor is provided. // Considers the first preceding sibling that is an element as the target. simulatePointerEnter(third); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.false; simulatePointerLeave(third); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTime(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.false; @@ -182,18 +190,18 @@ describe('Tooltip', () => { await elementUpdated(tooltip); simulatePointerEnter(first); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; simulatePointerLeave(first); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.false; // By providing an Element simulatePointerEnter(second); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.false; @@ -201,7 +209,7 @@ describe('Tooltip', () => { await elementUpdated(tooltip); simulatePointerEnter(second); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; }); @@ -378,12 +386,12 @@ describe('Tooltip', () => { await elementUpdated(tooltip); simulatePointerEnter(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; simulatePointerLeave(anchor); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.true; }); @@ -398,7 +406,7 @@ describe('Tooltip', () => { const closeIcon = tooltip.shadowRoot!.querySelector('igc-icon')!; simulateClick(closeIcon); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.false; }); @@ -418,7 +426,7 @@ describe('Tooltip', () => { }) ); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.false; }); @@ -426,14 +434,14 @@ describe('Tooltip', () => { describe('Methods` Tests', () => { beforeEach(async () => { - clock = useFakeTimers({ toFake: ['setTimeout'] }); + clock = vi.useFakeTimers({ toFake: ['setTimeout'] }); const container = await fixture(createTooltipWithTarget()); tooltip = container.querySelector(IgcTooltipComponent.tagName)!; anchor = container.querySelector('button')!; }); afterEach(() => { - clock.restore(); + clock.useRealTimers(); }); it('calls `show` and `hide` methods successfully and returns proper values', async () => { @@ -517,18 +525,18 @@ describe('Tooltip', () => { // the transient anchor should not reopen the tooltip once its hidden simulatePointerEnter(transientAnchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.false; simulatePointerEnter(defaultAnchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; }); it('should be able to pass and IDREF to `show` method', async () => { - const eventSpy = spy(tooltip, 'emitEvent'); + const spy = vi.spyOn(tooltip, 'emitEvent'); const [_, transientAnchor] = Array.from( tooltip.parentElement!.querySelectorAll('button') @@ -539,11 +547,11 @@ describe('Tooltip', () => { const result = await tooltip.show('custom-target'); expect(result).to.be.true; expect(tooltip.open).to.be.true; - expect(eventSpy.callCount).to.equal(0); + expect(spy).to.toHaveBeenCalledTimes(0); }); it('should correctly handle open state and events between default and transient anchors', async () => { - const eventSpy = spy(tooltip, 'emitEvent'); + const spy = vi.spyOn(tooltip, 'emitEvent'); const [defaultAnchor, transientAnchor] = Array.from( tooltip.parentElement!.querySelectorAll('button') @@ -552,30 +560,30 @@ describe('Tooltip', () => { const result = await tooltip.show(transientAnchor); expect(result).to.be.true; expect(tooltip.open).to.be.true; - expect(eventSpy.callCount).to.equal(0); + expect(spy).to.toHaveBeenCalledTimes(0); simulatePointerEnter(defaultAnchor); // Trigger on the initial default anchor. Tooltip must be hidden. expect(tooltip.open).to.be.false; - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; - expect(eventSpy).calledWith('igcOpening', { cancelable: true }); - expect(eventSpy).calledWith('igcOpened', { cancelable: false }); + expect(spy).toHaveBeenCalledWith('igcOpening', { cancelable: true }); + expect(spy).toHaveBeenCalledWith('igcOpened', { cancelable: false }); }); }); describe('Behaviors', () => { beforeEach(async () => { - clock = useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] }); + clock = vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] }); const container = await fixture(createTooltipWithTarget()); anchor = container.querySelector('button')!; tooltip = container.querySelector(IgcTooltipComponent.tagName)!; }); afterEach(() => { - clock.restore(); + clock.useRealTimers(); }); it('default triggers', async () => { @@ -583,12 +591,12 @@ describe('Tooltip', () => { expect(tooltip.hideTriggers).to.equal('pointerleave,click'); simulatePointerEnter(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; simulatePointerLeave(anchor); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.false; }); @@ -598,22 +606,22 @@ describe('Tooltip', () => { tooltip.hideTriggers = 'blur, click'; simulateFocus(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; simulateBlur(anchor); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.false; simulatePointerEnter(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; simulateClick(anchor); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.false; }); @@ -635,29 +643,29 @@ describe('Tooltip', () => { anchor = container.querySelector('button')!; simulateFocus(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; simulateBlur(anchor); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.false; simulateClick(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; simulateBlur(anchor); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.false; }); it('pointerenter over tooltip prevents hiding and pointerleave triggers hiding', async () => { simulatePointerEnter(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(); expect(tooltip.open).to.be.true; @@ -671,7 +679,7 @@ describe('Tooltip', () => { // Move cursor outside the tooltip simulatePointerLeave(tooltip); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(); expect(tooltip.open).to.be.false; }); @@ -679,82 +687,84 @@ describe('Tooltip', () => { it('should show/hide the tooltip based on `showDelay` and `hideDelay`', async () => { tooltip.showDelay = tooltip.hideDelay = 400; simulatePointerEnter(anchor); - await clock.tickAsync(399); + await clock.advanceTimersByTimeAsync(399); expect(tooltip.open).to.be.false; - await clock.tickAsync(1); + await clock.advanceTimersByTimeAsync(1); await showComplete(tooltip); expect(tooltip.open).to.be.true; simulatePointerLeave(anchor); - await clock.tickAsync(endTick(399)); + await clock.advanceTimersByTimeAsync(endTick(399)); expect(tooltip.open).to.be.true; - await clock.tickAsync(1); + await clock.advanceTimersByTimeAsync(1); await hideComplete(tooltip); expect(tooltip.open).to.be.false; }); it('prevents tooltip from showing when clicking the target - #1828', async () => { - const eventSpy = spy(tooltip, 'emitEvent'); + const spy = vi.spyOn(tooltip, 'emitEvent'); tooltip.showTriggers = 'pointerenter'; tooltip.hideTriggers = 'pointerleave'; simulatePointerEnter(anchor); - await clock.tickAsync(199); + await clock.advanceTimersByTimeAsync(199); expect(tooltip.open).to.be.false; // Click on the target before the tooltip is shown simulateClick(anchor); - await clock.tickAsync(1); + await clock.advanceTimersByTimeAsync(1); await showComplete(tooltip); expect(tooltip.open).to.be.false; - expect(eventSpy.callCount).to.equal(1); + expect(spy).toHaveBeenCalledTimes(1); - eventSpy.resetHistory(); + spy.mockClear(); // Does not prevent showing when showTriggers includes 'click' tooltip.showTriggers = 'click'; simulateClick(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(tooltip); expect(tooltip.open).to.be.true; simulatePointerLeave(anchor); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(tooltip); expect(tooltip.open).to.be.false; - expect(eventSpy.callCount).to.equal(4); + expect(spy).toHaveBeenCalledTimes(4); }); }); describe('Events', () => { - let eventSpy: ReturnType; + let spy: MockInstance; beforeEach(async () => { - clock = useFakeTimers({ toFake: ['setTimeout'] }); + clock = vi.useFakeTimers({ toFake: ['setTimeout'] }); const container = await fixture(createTooltipWithTarget()); tooltip = container.querySelector(IgcTooltipComponent.tagName)!; anchor = container.querySelector('button')!; - eventSpy = spy(tooltip, 'emitEvent'); + spy = vi.spyOn(tooltip, 'emitEvent'); }); afterEach(() => { - clock.restore(); + clock.useRealTimers(); }); const verifyStateAndEventSequence = ( state: { open: boolean } = { open: false } ) => { expect(tooltip.open).to.equal(state.open); - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith( + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith( + 1, state.open ? 'igcOpening' : 'igcClosing', { cancelable: true } ); - expect(eventSpy.secondCall).calledWith( + expect(spy).toHaveBeenNthCalledWith( + 2, state.open ? 'igcOpened' : 'igcClosed', { cancelable: false } ); @@ -762,14 +772,14 @@ describe('Tooltip', () => { it('events are correctly emitted on user interaction', async () => { simulatePointerEnter(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(tooltip); verifyStateAndEventSequence({ open: true }); - eventSpy.resetHistory(); + spy.mockClear(); simulatePointerLeave(anchor); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(tooltip); verifyStateAndEventSequence({ open: false }); }); @@ -780,13 +790,15 @@ describe('Tooltip', () => { }); simulatePointerEnter(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(tooltip); expect(tooltip.open).to.be.false; - expect(eventSpy).calledOnceWith('igcOpening', { cancelable: true }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcOpening', { + cancelable: true, + }); - eventSpy.resetHistory(); + spy.mockClear(); tooltip.open = true; await elementUpdated(tooltip); @@ -796,19 +808,21 @@ describe('Tooltip', () => { }); simulatePointerLeave(anchor); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(tooltip); expect(tooltip.open).to.be.true; - expect(eventSpy).calledOnceWith('igcClosing', { cancelable: true }); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcClosing', { + cancelable: true, + }); }); it('fires `igcClosed` when tooltip is hidden via Escape key', async () => { simulatePointerEnter(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(tooltip); - eventSpy.resetHistory(); + spy.mockClear(); document.documentElement.dispatchEvent( new KeyboardEvent('keydown', { @@ -818,22 +832,22 @@ describe('Tooltip', () => { }) ); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(tooltip); expect(tooltip.open).to.be.false; - expect(eventSpy.callCount).to.equal(1); - expect(eventSpy.firstCall).calledWith('igcClosed', { cancelable: false }); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith('igcClosed', { cancelable: false }); }); }); describe('Keyboard interactions', () => { beforeEach(async () => { - clock = useFakeTimers({ toFake: ['setTimeout'] }); + clock = vi.useFakeTimers({ toFake: ['setTimeout'] }); }); afterEach(() => { - clock.restore(); + clock.useRealTimers(); }); it('pressing Escape in an active page hides the tooltip', async () => { @@ -844,7 +858,7 @@ describe('Tooltip', () => { ); simulatePointerEnter(anchor); - await clock.tickAsync(DEFAULT_SHOW_DELAY); + await clock.advanceTimersByTimeAsync(DEFAULT_SHOW_DELAY); await showComplete(tooltip); expect(tooltip.open).to.be.true; @@ -856,7 +870,7 @@ describe('Tooltip', () => { composed: true, }) ); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(tooltip); expect(tooltip.open).to.be.false; @@ -882,7 +896,7 @@ describe('Tooltip', () => { }) ); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(first); await hideComplete(last); @@ -897,7 +911,7 @@ describe('Tooltip', () => { }) ); - await clock.tickAsync(endTick(DEFAULT_HIDE_DELAY)); + await clock.advanceTimersByTimeAsync(endTick(DEFAULT_HIDE_DELAY)); await hideComplete(first); await hideComplete(last); diff --git a/src/components/tooltip/tooltip.ts b/src/components/tooltip/tooltip.ts index 8fd732ccf..8b0406250 100644 --- a/src/components/tooltip/tooltip.ts +++ b/src/components/tooltip/tooltip.ts @@ -97,7 +97,7 @@ export default class IgcTooltipComponent extends EventEmitterMixin< easing: EaseOut.Sine, }); - private _timeoutId?: number; + private _timeoutId?: ReturnType; private _autoHideDelay = 180; private _showDelay = 200; private _hideDelay = 300; diff --git a/src/components/tree/tree-navigation.spec.ts b/src/components/tree/tree-navigation.spec.ts index 141011fc5..8632546fa 100644 --- a/src/components/tree/tree-navigation.spec.ts +++ b/src/components/tree/tree-navigation.spec.ts @@ -1,7 +1,15 @@ -import { elementUpdated, expect, waitUntil } from '@open-wc/testing'; -import { spy } from 'sinon'; - -import { defineComponents } from '../../index.js'; +import { + beforeAll, + beforeEach, + describe, + expect, + it, + type MockInstance, + vi, +} from 'vitest'; +import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, waitUntil } from '../common/helpers.spec.js'; +import { eventMatch } from '../common/utils.spec.js'; import type { TreeSelectionEventInit } from './tree.common.js'; import IgcTreeComponent from './tree.js'; import type { IgcTreeNavigationService } from './tree.navigation.js'; @@ -9,20 +17,20 @@ import IgcTreeItemComponent from './tree-item.js'; import { navigationTree, SLOTS, TreeTestFunctions } from './tree-utils.spec.js'; describe('Tree Navigation', () => { - before(() => { + beforeAll(() => { defineComponents(IgcTreeItemComponent, IgcTreeComponent); }); let tree: IgcTreeComponent; let treeNavService: IgcTreeNavigationService; let topLevelItems: IgcTreeItemComponent[]; - let eventSpy: any; + let spy: MockInstance; beforeEach(async () => { tree = await TreeTestFunctions.createTreeElement(navigationTree); treeNavService = tree.navService; topLevelItems = tree.items.filter((i) => i.level === 0); - eventSpy = spy(tree, 'emitEvent'); + spy = vi.spyOn(tree, 'emitEvent'); }); it('Should focus and activate the first tree item on Home key press and the last tree item on End key press', async () => { @@ -38,10 +46,10 @@ describe('Tree Navigation', () => { expect(treeNavService.activeItem).to.equal(tree.items[0]); expect(treeNavService.focusedItem).to.equal(tree.items[0]); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: tree.items[0], }); - eventSpy.resetHistory(); + spy.mockClear(); treeNavService.focusedItem?.dispatchEvent( new KeyboardEvent('keydown', { @@ -59,10 +67,10 @@ describe('Tree Navigation', () => { expect(treeNavService.activeItem).to.equal(lastItem); expect(treeNavService.focusedItem).to.equal(lastItem); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: lastItem, }); - eventSpy.resetHistory(); + spy.mockClear(); }); it('Should not navigate when a tree item has no parent and item is collapsed on Arrow Left key press', async () => { @@ -86,7 +94,7 @@ describe('Tree Navigation', () => { expect(topLevelItems[0].expanded).to.be.false; expect(treeNavService.activeItem).to.equal(topLevelItems[0]); expect(treeNavService.focusedItem).to.equal(topLevelItems[0]); - expect(eventSpy.called).to.be.false; + expect(spy).toHaveBeenCalledTimes(0); }); it('Should navigate to the parent item of a tree item w/ expanded === true on Arrow Left key press, moving focus and active', async () => { @@ -106,10 +114,10 @@ describe('Tree Navigation', () => { expect(treeNavService.activeItem).to.equal(item2); expect(treeNavService.focusedItem).to.equal(item2); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: item2, }); - eventSpy.resetHistory(); + spy.mockClear(); // Should collapse expanded tree items on Arrow Left key press item2.dispatchEvent( @@ -119,7 +127,7 @@ describe('Tree Navigation', () => { cancelable: true, }) ); - await waitUntil(() => eventSpy.calledWith('igcItemCollapsed')); + await waitUntil(() => eventMatch(spy, 'igcItemCollapsed')); expect(item2.active).to.be.true; expect(item2.expanded).to.be.false; @@ -128,13 +136,13 @@ describe('Tree Navigation', () => { detail: item2, cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcItemCollapsing', collapsingArgs); - expect(eventSpy.secondCall).calledWith('igcItemCollapsed', { + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcItemCollapsing', collapsingArgs); + expect(spy).toHaveBeenNthCalledWith(2, 'igcItemCollapsed', { detail: item2, }); - eventSpy.resetHistory(); + spy.mockClear(); }); it('Should not navigate when a tree item has no children on Arrow Right key press', async () => { @@ -150,7 +158,7 @@ describe('Tree Navigation', () => { expect(item4.active).to.be.true; expect(treeNavService.activeItem).to.equal(item4); expect(treeNavService.focusedItem).to.equal(item4); - expect(eventSpy.called).to.be.false; + expect(spy).toHaveBeenCalledTimes(0); }); it('Should navigate to the first child of an expanded on Arrow Right key press, moving focus and active', async () => { @@ -167,10 +175,10 @@ describe('Tree Navigation', () => { expect(treeNavService.activeItem).to.equal(item2Children[0]); expect(treeNavService.focusedItem).to.equal(item2Children[0]); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: item2Children[0], }); - eventSpy.resetHistory(); + spy.mockClear(); const item21Children = item2Children[0].getChildren(); @@ -187,7 +195,7 @@ describe('Tree Navigation', () => { expect(treeNavService.activeItem).to.equal(item21Children[0]); expect(treeNavService.focusedItem).to.equal(item21Children[0]); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: item21Children[0], }); }); @@ -202,7 +210,7 @@ describe('Tree Navigation', () => { await elementUpdated(tree); TreeTestFunctions.setFocusAndTriggerKeydown(item1, tree, 'ArrowRight'); - await waitUntil(() => eventSpy.calledWith('igcItemExpanded')); + await waitUntil(() => eventMatch(spy, 'igcItemExpanded')); expect(item1.expanded).to.be.true; @@ -210,9 +218,9 @@ describe('Tree Navigation', () => { detail: item1, cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcItemExpanding', expandingArgs); - expect(eventSpy.secondCall).calledWith('igcItemExpanded', { + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcItemExpanding', expandingArgs); + expect(spy).toHaveBeenNthCalledWith(2, 'igcItemExpanded', { detail: item1, }); }); @@ -236,10 +244,10 @@ describe('Tree Navigation', () => { expect(treeNavService.activeItem).to.equal(item3Hierarchy[i + 1]); expect(treeNavService.focusedItem).to.equal(item3Hierarchy[i + 1]); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: item3Hierarchy[i + 1], }); - eventSpy.resetHistory(); + spy.mockClear(); } // Arrow Down on last tree item @@ -251,7 +259,7 @@ describe('Tree Navigation', () => { expect(treeNavService.activeItem).to.equal(item4); expect(treeNavService.focusedItem).to.equal(item4); - expect(eventSpy.called).to.be.false; + expect(spy).toHaveBeenCalledTimes(0); }); it('Should only focus the next visible tree item on Arrow Down + Ctrl key press', async () => { @@ -276,7 +284,7 @@ describe('Tree Navigation', () => { expect(item3Hierarchy[i + 1].active).to.be.false; expect(treeNavService.activeItem).to.equal(item3Hierarchy[0]); expect(treeNavService.focusedItem).to.equal(item3Hierarchy[i + 1]); - expect(eventSpy.called).to.be.false; + expect(spy).toHaveBeenCalledTimes(0); } }); @@ -298,10 +306,10 @@ describe('Tree Navigation', () => { expect(item3Hierarchy[i - 1].active).to.be.true; expect(treeNavService.activeItem).to.equal(item3Hierarchy[i - 1]); expect(treeNavService.focusedItem).to.equal(item3Hierarchy[i - 1]); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: item3Hierarchy[i - 1], }); - eventSpy.resetHistory(); + spy.mockClear(); } // Arrow Up on first tree item @@ -313,7 +321,7 @@ describe('Tree Navigation', () => { expect(treeNavService.activeItem).to.equal(item1); expect(treeNavService.focusedItem).to.equal(item1); - expect(eventSpy.called).to.be.false; + expect(spy).toHaveBeenCalledTimes(0); }); it('Should only focus the previous visible tree item on Arrow Up + Ctrl key press', async () => { @@ -338,7 +346,7 @@ describe('Tree Navigation', () => { item3Hierarchy[item3Hierarchy.length - 1] ); expect(treeNavService.focusedItem).to.equal(item3Hierarchy[i - 1]); - expect(eventSpy.called).to.be.false; + expect(spy).toHaveBeenCalledTimes(0); } }); @@ -351,7 +359,7 @@ describe('Tree Navigation', () => { item3.active = true; await elementUpdated(tree); TreeTestFunctions.setFocusAndTriggerKeydown(item3, tree, '*'); - await waitUntil(() => eventSpy.calledWith('igcItemExpanded')); + await waitUntil(() => eventMatch(spy, 'igcItemExpanded')); expect(topLevelItems[3].expanded).to.be.false; // Item4 does not have children => not expanded topLevelItems.pop(); @@ -386,13 +394,13 @@ describe('Tree Navigation', () => { cancelable: true, }; // Item2 is already expanded, and Item4 has no children => no expanding events emitted for them - expect(eventSpy.callCount).to.equal(4); - expect(eventSpy.firstCall).calledWith('igcItemExpanding', expandingArgs1); - expect(eventSpy.secondCall).calledWith('igcItemExpanding', expandingArgs2); - expect(eventSpy.thirdCall).calledWith('igcItemExpanded', { + expect(spy).toHaveBeenCalledTimes(4); + expect(spy).toHaveBeenNthCalledWith(1, 'igcItemExpanding', expandingArgs1); + expect(spy).toHaveBeenNthCalledWith(2, 'igcItemExpanding', expandingArgs2); + expect(spy).toHaveBeenNthCalledWith(3, 'igcItemExpanded', { detail: topLevelItems[0], }); - expect(eventSpy.lastCall).calledWith('igcItemExpanded', { + expect(spy).toHaveBeenNthCalledWith(4, 'igcItemExpanded', { detail: item3, }); }); @@ -416,7 +424,7 @@ describe('Tree Navigation', () => { expect(treeNavService.activeItem).to.equal(topLevelItems[0]); expect(treeNavService.focusedItem).to.equal(topLevelItems[0]); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: topLevelItems[0], }); }); @@ -443,12 +451,12 @@ describe('Tree Navigation', () => { }, cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcActiveItem', { + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcActiveItem', { detail: topLevelItems[0], }); - expect(eventSpy.secondCall).calledWith('igcSelection', args); - eventSpy.resetHistory(); + expect(spy).toHaveBeenNthCalledWith(2, 'igcSelection', args); + spy.mockClear(); const expectedSelection: IgcTreeItemComponent[] = []; @@ -472,8 +480,8 @@ describe('Tree Navigation', () => { cancelable: true, }; - expect(eventSpy.callCount).to.equal(1); - expect(eventSpy.firstCall).calledWith('igcSelection', args); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenNthCalledWith(1, 'igcSelection', args); }); it("Should only activate the tree item when tree.selection === 'None' on Space key press and also select it when tree.selection !== 'None'", async () => { @@ -494,11 +502,11 @@ describe('Tree Navigation', () => { expect(treeNavService.activeItem).to.equal(topLevelItems[0]); expect(treeNavService.focusedItem).to.equal(topLevelItems[0]); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: topLevelItems[0], }); - eventSpy.resetHistory(); + spy.mockClear(); tree.selection = 'multiple'; await elementUpdated(tree); @@ -516,11 +524,11 @@ describe('Tree Navigation', () => { }, cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcActiveItem', { + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcActiveItem', { detail: topLevelItems[1], }); - expect(eventSpy.secondCall).calledWith('igcSelection', args); + expect(spy).toHaveBeenNthCalledWith(2, 'igcSelection', args); }); it("Should select item range when tree.selection !== 'None' on Space + Shift keys press, moving active", async () => { @@ -549,12 +557,12 @@ describe('Tree Navigation', () => { }, cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcActiveItem', { + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcActiveItem', { detail: topLevelItems[0], }); - expect(eventSpy.secondCall).calledWith('igcSelection', args); - eventSpy.resetHistory(); + expect(spy).toHaveBeenNthCalledWith(2, 'igcSelection', args); + spy.mockClear(); const expectedSelection = [ topLevelItems[0], @@ -582,11 +590,11 @@ describe('Tree Navigation', () => { cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcActiveItem', { + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcActiveItem', { detail: topLevelItems[1], }); - expect(eventSpy.secondCall).calledWith('igcSelection', args); + expect(spy).toHaveBeenNthCalledWith(2, 'igcSelection', args); }); it('Should assign proper tabIndex for tree item labels containing tabbable elements on focus', async () => { diff --git a/src/components/tree/tree-selection.spec.ts b/src/components/tree/tree-selection.spec.ts index ea178dcc9..41accc97b 100644 --- a/src/components/tree/tree-selection.spec.ts +++ b/src/components/tree/tree-selection.spec.ts @@ -1,7 +1,15 @@ -import { elementUpdated, expect } from '@open-wc/testing'; -import { spy } from 'sinon'; - -import { defineComponents, type IgcCheckboxComponent } from '../../index.js'; +import { + beforeAll, + beforeEach, + describe, + expect, + it, + type MockInstance, + vi, +} from 'vitest'; +import type IgcCheckboxComponent from '../checkbox/checkbox.js'; +import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated } from '../common/helpers.spec.js'; import type { TreeSelectionEventInit } from './tree.common.js'; import IgcTreeComponent from './tree.js'; import type { IgcTreeSelectionService } from './tree.selection.js'; @@ -15,7 +23,7 @@ import { } from './tree-utils.spec.js'; describe('Tree Selection', () => { - before(() => { + beforeAll(() => { defineComponents(IgcTreeItemComponent, IgcTreeComponent); }); @@ -176,14 +184,14 @@ describe('Tree Selection', () => { describe('Multiple', () => { let topLevelItems: IgcTreeItemComponent[]; - let eventSpy: any; + let spy: MockInstance; beforeEach(async () => { tree = await TreeTestFunctions.createTreeElement(selectedItemsTree); treeSelectionService = tree.selectionService; initialSelection = tree.items.filter((item) => item.selected === true); topLevelItems = tree.items.filter((i) => i.level === 0); - eventSpy = spy(tree, 'emitEvent'); + spy = vi.spyOn(tree, 'emitEvent'); }); it('Should be able to set item.selected correctly', async () => { @@ -224,8 +232,8 @@ describe('Tree Selection', () => { }, cancelable: true, }; - expect(eventSpy).calledWith('igcSelection', args); - eventSpy.resetHistory(); + expect(spy).toHaveBeenCalledWith('igcSelection', args); + spy.mockClear(); const item12 = topLevelItems[0].getChildren()[1]; TreeTestFunctions.verifyItemSelection(item12, false); @@ -247,8 +255,8 @@ describe('Tree Selection', () => { }, cancelable: true, }; - expect(eventSpy).calledOnceWith('igcSelection', args); - eventSpy.resetHistory(); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcSelection', args); + spy.mockClear(); }); it('Should be able to prevent the igcSelection event.', async () => { @@ -275,7 +283,7 @@ describe('Tree Selection', () => { cancelable: true, }; - expect(eventSpy).calledOnceWith('igcSelection', args); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcSelection', args); TreeTestFunctions.verifyItemSelection(item12, false); tree.items.forEach((item) => { @@ -315,8 +323,8 @@ describe('Tree Selection', () => { await elementUpdated(tree); TreeTestFunctions.verifyItemSelection(topLevelItems[0], true); - expect(eventSpy).calledOnceWith('igcSelection', args); - eventSpy.resetHistory(); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcSelection', args); + spy.mockClear(); const endOfSelectionRage = topLevelItems[1].getChildren()[1]; @@ -344,8 +352,8 @@ describe('Tree Selection', () => { } }); - expect(eventSpy).calledOnceWith('igcSelection', args); - eventSpy.resetHistory(); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcSelection', args); + spy.mockClear(); // Select the same range and verify no event is emitted selectionPart = expectedSelection[0].shadowRoot!.querySelector( @@ -355,7 +363,7 @@ describe('Tree Selection', () => { cb?.dispatchEvent(new MouseEvent('click', { shiftKey: true })); await elementUpdated(tree); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); }); it('Should select a single item when there are no selected items and selction is performed with Shift + click', async () => { @@ -377,8 +385,8 @@ describe('Tree Selection', () => { await elementUpdated(tree); TreeTestFunctions.verifyItemSelection(topLevelItems[2], true); - expect(eventSpy).calledOnceWith('igcSelection', args); - eventSpy.resetHistory(); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcSelection', args); + spy.mockClear(); }); }); @@ -689,7 +697,7 @@ describe('Tree Selection', () => { }); it('Should be able to prevent the igcSelection event', async () => { - const eventSpy = spy(tree, 'emitEvent'); + const spy = vi.spyOn(tree, 'emitEvent'); const item2Children = topLevelItems[1].getChildren(); const item211 = item2Children[0].getChildren()[0]; @@ -714,7 +722,7 @@ describe('Tree Selection', () => { cancelable: true, }; - expect(eventSpy).calledOnceWith('igcSelection', args); + expect(spy).toHaveBeenCalledExactlyOnceWith('igcSelection', args); tree.items.forEach((item) => { if (initialSelection.indexOf(item) === -1) { diff --git a/src/components/tree/tree-utils.spec.ts b/src/components/tree/tree-utils.spec.ts index 37000a929..3d312e169 100644 --- a/src/components/tree/tree-utils.spec.ts +++ b/src/components/tree/tree-utils.spec.ts @@ -1,4 +1,5 @@ -import { expect, fixture, html, unsafeStatic } from '@open-wc/testing'; +import { expect } from 'vitest'; +import { fixture, html, unsafeStatic } from '../common/helpers.spec.js'; import type IgcTreeComponent from './tree.js'; import type IgcTreeItemComponent from './tree-item.js'; diff --git a/src/components/tree/tree.spec.ts b/src/components/tree/tree.spec.ts index 67b6de4aa..66964d4dc 100644 --- a/src/components/tree/tree.spec.ts +++ b/src/components/tree/tree.spec.ts @@ -1,9 +1,16 @@ -import { aTimeout, elementUpdated, expect, waitUntil } from '@open-wc/testing'; -import { spy } from 'sinon'; - +import { + beforeAll, + beforeEach, + describe, + expect, + it, + type MockInstance, + vi, +} from 'vitest'; import type IgcCheckboxComponent from '../checkbox/checkbox.js'; import { defineComponents } from '../common/definitions/defineComponents.js'; -import { scrolledIntoView } from '../common/utils.spec.js'; +import { aTimeout, elementUpdated, waitUntil } from '../common/helpers.spec.js'; +import { eventMatch, scrolledIntoView } from '../common/utils.spec.js'; import IgcTreeComponent from './tree.js'; import type IgcTreeItemComponent from './tree-item.js'; import { @@ -22,7 +29,7 @@ import { } from './tree-utils.spec.js'; describe('Tree', () => { - before(() => { + beforeAll(() => { defineComponents(IgcTreeComponent); }); @@ -370,7 +377,7 @@ describe('Tree', () => { it('Should emit igcActiveItem event when the active item changes', async () => { tree = await TreeTestFunctions.createTreeElement(simpleHierarchyTree); - const eventSpy = spy(tree, 'emitEvent'); + const spy = vi.spyOn(tree, 'emitEvent'); await elementUpdated(tree); tree.items.forEach((item) => { @@ -381,25 +388,25 @@ describe('Tree', () => { tree.items[0].dispatchEvent(new PointerEvent('click')); await elementUpdated(tree); expect(tree.navService.activeItem).to.equal(tree.items[0]); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: tree.items[0], }); - eventSpy.resetHistory(); + spy.mockClear(); tree.navService.setActiveItem(tree.items[1], true); await elementUpdated(tree); expect(tree.navService.activeItem).to.equal(tree.items[1]); - expect(eventSpy).calledOnceWithExactly('igcActiveItem', { + expect(spy).toHaveBeenCalledExactlyOnceWith('igcActiveItem', { detail: tree.items[1], }); - eventSpy.resetHistory(); + spy.mockClear(); tree.navService.setActiveItem(tree.items[2], false); await elementUpdated(tree); expect(tree.navService.activeItem).to.equal(tree.items[2]); - expect(eventSpy).not.called; + expect(spy).not.toHaveBeenCalled(); }); it('Should activate the last tree item set as active if there are multiple', async () => { @@ -505,19 +512,19 @@ describe('Tree', () => { describe('Expand/Collapse', async () => { let topLevelItems: IgcTreeItemComponent[]; - let eventSpy: any; + let spy: MockInstance; beforeEach(async () => { tree = await TreeTestFunctions.createTreeElement(expandCollapseTree); topLevelItems = tree.items.filter((i) => i.level === 0); - eventSpy = spy(tree, 'emitEvent'); + spy = vi.spyOn(tree, 'emitEvent'); }); it('Should expand all collapsed (including the disabled) items w/ tree.expand()', async () => { tree.expand(); await elementUpdated(tree); - expect(eventSpy.called).to.be.false; // event is not emitted when expanding through API + expect(spy).not.toHaveBeenCalled(); // event is not emitted when expanding through API tree.items.forEach((item) => { expect(item.expanded).to.be.true; }); @@ -529,7 +536,7 @@ describe('Tree', () => { tree.expand(items); await elementUpdated(tree); - expect(eventSpy.called).to.be.false; // event is not emitted when expanding through API + expect(spy).not.toHaveBeenCalled(); // event is not emitted when expanding through API items.forEach((item) => { expect(item.expanded).to.be.true; }); @@ -539,7 +546,7 @@ describe('Tree', () => { tree.collapse(); await elementUpdated(tree); - expect(eventSpy.called).to.be.false; // event is not emitted when collapsing through API + expect(spy).not.toHaveBeenCalled(); // event is not emitted when collapsing through API tree.items.forEach((item) => { expect(item.expanded).to.be.false; }); @@ -551,7 +558,7 @@ describe('Tree', () => { tree.collapse(items); await elementUpdated(tree); - expect(eventSpy.called).to.be.false; // event is not emitted when collapsing through API + expect(spy).not.toHaveBeenCalled(); // event is not emitted when collapsing through API items.forEach((item) => { expect(item.expanded).to.be.false; }); @@ -568,7 +575,7 @@ describe('Tree', () => { ); item2IndSlot?.dispatchEvent(new MouseEvent('click')); - await waitUntil(() => eventSpy.calledWith('igcItemCollapsed')); + await waitUntil(() => eventMatch(spy, 'igcItemCollapsed')); TreeTestFunctions.verifyExpansionState(topLevelItems[1], false); @@ -577,16 +584,17 @@ describe('Tree', () => { detail: topLevelItems[1], cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith( + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith( + 1, 'igcItemCollapsing', collapsingArgs ); - expect(eventSpy.secondCall).calledWith('igcItemCollapsed', { + expect(spy).toHaveBeenNthCalledWith(2, 'igcItemCollapsed', { detail: topLevelItems[1], }); - eventSpy.resetHistory(); + spy.mockClear(); const item21IndSlot = TreeTestFunctions.getSlot( childItem21, @@ -599,7 +607,7 @@ describe('Tree', () => { // Should not collapse disabled item expect(childItem21.disabled).to.be.true; TreeTestFunctions.verifyExpansionState(childItem21, true); - expect(eventSpy.called).to.be.false; + expect(spy).not.toHaveBeenCalled(); }); it('Should expand items when user interacts w/ indicator and item.expanded === true', async () => { @@ -613,7 +621,7 @@ describe('Tree', () => { ); item1IndSlot?.dispatchEvent(new MouseEvent('click')); - await waitUntil(() => eventSpy.calledWith('igcItemExpanded')); + await waitUntil(() => eventMatch(spy, 'igcItemExpanded')); TreeTestFunctions.verifyExpansionState(topLevelItems[0], true); @@ -622,13 +630,13 @@ describe('Tree', () => { detail: topLevelItems[0], cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcItemExpanding', expandingArgs); - expect(eventSpy.secondCall).calledWith('igcItemExpanded', { + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcItemExpanding', expandingArgs); + expect(spy).toHaveBeenNthCalledWith(2, 'igcItemExpanded', { detail: topLevelItems[0], }); - eventSpy.resetHistory(); + spy.mockClear(); const item11IndSlot = TreeTestFunctions.getSlot( childItem11, @@ -641,7 +649,7 @@ describe('Tree', () => { // Should not expand disabled item expect(childItem11.disabled).to.be.true; TreeTestFunctions.verifyExpansionState(childItem11, false); - expect(eventSpy.called).to.be.false; + expect(spy).not.toHaveBeenCalled(); }); it('Should collapse items when item.expanded is set to false', async () => { @@ -652,7 +660,7 @@ describe('Tree', () => { TreeTestFunctions.verifyExpansionState(topLevelItems[1], false); // Should not emit event when collapsed through API - expect(eventSpy.called).to.be.false; + expect(spy).not.toHaveBeenCalled(); }); it('Should expand items when item.expanded is set to true', async () => { @@ -663,7 +671,7 @@ describe('Tree', () => { TreeTestFunctions.verifyExpansionState(topLevelItems[0], true); // Should not emit event when collapsed through API - expect(eventSpy.called).to.be.false; + expect(spy).not.toHaveBeenCalled(); }); it('Should expand items when item.expand() is called', async () => { @@ -674,14 +682,14 @@ describe('Tree', () => { TreeTestFunctions.verifyExpansionState(topLevelItems[0], true); // Should not emit event when collapsed through API - expect(eventSpy.called).to.be.false; + expect(spy).not.toHaveBeenCalled(); // Should not expand with event an already expanded item topLevelItems[0].expandWithEvent(); await elementUpdated(tree); TreeTestFunctions.verifyExpansionState(topLevelItems[0], true); - expect(eventSpy.called).to.be.false; + expect(spy).not.toHaveBeenCalled(); }); it('Should collapse items when item.collapse() is called', async () => { @@ -692,7 +700,7 @@ describe('Tree', () => { TreeTestFunctions.verifyExpansionState(topLevelItems[1], false); // Should not emit event when collapsed through API - expect(eventSpy.called).to.be.false; + expect(spy).not.toHaveBeenCalled(); }); it('Should expand/collapse nodes when clicking over them if `toggleNodeOnClick` is set to `true`', async () => { @@ -705,38 +713,39 @@ describe('Tree', () => { await elementUpdated(tree); TreeTestFunctions.verifyExpansionState(topLevelItems[0], true); - await waitUntil(() => eventSpy.calledWith('igcItemExpanded')); + await waitUntil(() => eventMatch(spy, 'igcItemExpanded')); // Should emit ing and ed events when item state is toggled through UI const expandingArgs = { detail: topLevelItems[0], cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcItemExpanding', expandingArgs); - expect(eventSpy.secondCall).calledWith('igcItemExpanded', { + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcItemExpanding', expandingArgs); + expect(spy).toHaveBeenNthCalledWith(2, 'igcItemExpanded', { detail: topLevelItems[0], }); - eventSpy.resetHistory(); + spy.mockClear(); topLevelItems[0].dispatchEvent(new MouseEvent('click')); await elementUpdated(tree); TreeTestFunctions.verifyExpansionState(topLevelItems[0], false); - await waitUntil(() => eventSpy.calledWith('igcItemCollapsed')); + await waitUntil(() => eventMatch(spy, 'igcItemCollapsed')); // Should emit ing and ed events when item state is toggled through UI const collapsingArgs = { detail: topLevelItems[0], cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith( + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith( + 1, 'igcItemCollapsing', collapsingArgs ); - expect(eventSpy.secondCall).calledWith('igcItemCollapsed', { + expect(spy).toHaveBeenNthCalledWith(2, 'igcItemCollapsed', { detail: topLevelItems[0], }); }); @@ -791,13 +800,13 @@ describe('Tree', () => { TreeTestFunctions.verifyExpansionState(topLevelItems[1], false); // Should not emit event when collapsed through API - expect(eventSpy.called).to.be.false; + expect(spy).not.toHaveBeenCalled(); topLevelItems[1].toggle(); await elementUpdated(tree); TreeTestFunctions.verifyExpansionState(topLevelItems[1], true); - expect(eventSpy.called).to.be.false; + expect(spy).not.toHaveBeenCalled(); }); it('Should be able to prevent the expansion/collapsing through the ing events.', async () => { @@ -819,9 +828,9 @@ describe('Tree', () => { detail: topLevelItems[0], cancelable: true, }; - expect(eventSpy.callCount).to.equal(1); - expect(eventSpy.firstCall).calledWith('igcItemExpanding', expandingArgs); - eventSpy.resetHistory(); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenNthCalledWith(1, 'igcItemExpanding', expandingArgs); + spy.mockClear(); const item2IndSlot = TreeTestFunctions.getSlot( topLevelItems[1], @@ -841,8 +850,9 @@ describe('Tree', () => { detail: topLevelItems[1], cancelable: true, }; - expect(eventSpy.callCount).to.equal(1); - expect(eventSpy.firstCall).calledWith( + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenNthCalledWith( + 1, 'igcItemCollapsing', collapsingArgs ); @@ -999,13 +1009,13 @@ describe('Tree', () => { describe('Disabled item', async () => { let disabledItems: IgcTreeItemComponent[]; let topLevelItems: IgcTreeItemComponent[]; - let eventSpy: any; + let spy: MockInstance; beforeEach(async () => { tree = await TreeTestFunctions.createTreeElement(disabledItemsTree); topLevelItems = tree.items.filter((i) => i.level === 0); disabledItems = tree.items.filter((i) => i.disabled === true); - eventSpy = spy(tree, 'emitEvent'); + spy = vi.spyOn(tree, 'emitEvent'); }); it('Should be able to select/activate/expand disabled item through API', async () => { @@ -1023,7 +1033,7 @@ describe('Tree', () => { expect(cb.checked).to.be.true; expect(cb.indeterminate).to.be.false; - expect(eventSpy).not.to.be.called; // event not emitted when interacting through API + expect(spy).not.toHaveBeenCalled(); // event not emitted when interacting through API const item11 = disabledItems[1]; expect(item11.disabled).to.be.true; @@ -1034,7 +1044,7 @@ describe('Tree', () => { TreeTestFunctions.verifyItemSelection(item11, true); expect(cb.checked).to.be.true; expect(cb.indeterminate).to.be.false; - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); const item12 = disabledItems[2]; expect(item12.disabled).to.be.true; @@ -1044,13 +1054,13 @@ describe('Tree', () => { await elementUpdated(tree); expect(item12.active).to.be.true; - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); item12.expand(); await elementUpdated(tree); expect(item12.expanded).to.be.true; - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); }); it('Should not be able to interact with disabled item through UI', async () => { @@ -1066,7 +1076,7 @@ describe('Tree', () => { TreeTestFunctions.verifyExpansionState(item11, true); expect(item11.active).to.be.false; - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); expect(tree.navService.focusedItem).not.to.equal(item11); const item11LabelSlot = TreeTestFunctions.getSlot(item11, SLOTS.label); @@ -1077,14 +1087,14 @@ describe('Tree', () => { TreeTestFunctions.verifyExpansionState(item11, true); expect(tree.navService.focusedItem).not.to.equal(item11); expect(item11.active).to.be.false; - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); item11.dispatchEvent(new MouseEvent('click')); await elementUpdated(tree); expect(item11.active).to.be.false; expect(tree.navService.focusedItem).not.to.equal(item11); - expect(eventSpy).not.to.be.called; + expect(spy).not.toHaveBeenCalled(); item11.dispatchEvent(new Event('focus')); await elementUpdated(tree); @@ -1288,12 +1298,12 @@ describe('Tree', () => { describe('RTL', () => { let topLevelItems: IgcTreeItemComponent[]; - let eventSpy: any; + let spy: MockInstance; beforeEach(async () => { tree = await TreeTestFunctions.createTreeElement(navigationTree); topLevelItems = tree.items.filter((i) => i.level === 0); - eventSpy = spy(tree, 'emitEvent'); + spy = vi.spyOn(tree, 'emitEvent'); tree.dir = 'rtl'; await elementUpdated(tree); }); @@ -1304,7 +1314,7 @@ describe('Tree', () => { await elementUpdated(tree); TreeTestFunctions.setFocusAndTriggerKeydown(item2, tree, 'ArrowRight'); - await waitUntil(() => eventSpy.calledWith('igcItemCollapsed')); + await waitUntil(() => eventMatch(spy, 'igcItemCollapsed')); expect(item2.expanded).to.be.false; @@ -1312,12 +1322,13 @@ describe('Tree', () => { detail: item2, cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith( + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith( + 1, 'igcItemCollapsing', collapsingArgs ); - expect(eventSpy.secondCall).calledWith('igcItemCollapsed', { + expect(spy).toHaveBeenNthCalledWith(2, 'igcItemCollapsed', { detail: item2, }); }); @@ -1332,7 +1343,7 @@ describe('Tree', () => { await elementUpdated(tree); TreeTestFunctions.setFocusAndTriggerKeydown(item1, tree, 'ArrowLeft'); - await waitUntil(() => eventSpy.calledWith('igcItemExpanded')); + await waitUntil(() => eventMatch(spy, 'igcItemExpanded')); expect(item1.expanded).to.be.true; @@ -1340,9 +1351,9 @@ describe('Tree', () => { detail: item1, cancelable: true, }; - expect(eventSpy.callCount).to.equal(2); - expect(eventSpy.firstCall).calledWith('igcItemExpanding', expandingArgs); - expect(eventSpy.secondCall).calledWith('igcItemExpanded', { + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, 'igcItemExpanding', expandingArgs); + expect(spy).toHaveBeenNthCalledWith(2, 'igcItemExpanded', { detail: item1, }); }); diff --git a/src/components/validation-container/validation-container.spec.ts b/src/components/validation-container/validation-container.spec.ts index cb65141a6..ac61eb327 100644 --- a/src/components/validation-container/validation-container.spec.ts +++ b/src/components/validation-container/validation-container.spec.ts @@ -1,6 +1,7 @@ -import { elementUpdated, fixture, html } from '@open-wc/testing'; -import type { TemplateResult } from 'lit'; +import { html, type TemplateResult } from 'lit'; +import { beforeAll, describe, it } from 'vitest'; import { defineComponents } from '../common/definitions/defineComponents.js'; +import { elementUpdated, fixture } from '../common/helpers.spec.js'; import { ValidityHelpers } from '../common/validity-helpers.spec.js'; import IgcInputComponent from '../input/input.js'; @@ -10,7 +11,7 @@ describe('Validation container', () => { const helperSlot = 'helper-text'; const valueMissingSlot = 'value-missing'; - before(() => { + beforeAll(() => { defineComponents(IgcInputComponent); }); diff --git a/src/theming/config.spec.ts b/src/theming/config.spec.ts index ef09f7d9a..466282e7c 100644 --- a/src/theming/config.spec.ts +++ b/src/theming/config.spec.ts @@ -1,5 +1,5 @@ -import { aTimeout, expect, oneEvent } from '@open-wc/testing'; - +import { describe, expect, it } from 'vitest'; +import { aTimeout } from '../components/common/helpers.spec.js'; import { configureTheme, getTheme } from './config.js'; import { CHANGE_THEME_EVENT } from './theming-event.js'; import { getAllCssVariables } from './utils.js'; @@ -37,12 +37,20 @@ describe('Theming Config', () => { const theme = 'material'; const themeVariant = 'light'; + const eventPromise = new Promise((resolve) => { + window.addEventListener( + CHANGE_THEME_EVENT, + (e) => resolve(e as CustomEvent), + { once: true } + ); + }); + setTimeout(() => { configureTheme(theme, themeVariant); expect(getTheme()).to.deep.equal({ theme, themeVariant }); }); - const { detail } = await oneEvent(window, CHANGE_THEME_EVENT); + const { detail } = await eventPromise; expect(detail.theme).to.equal(theme); expect(detail.themeVariant).to.equal(themeVariant); }); diff --git a/src/vitest-browser.d.ts b/src/vitest-browser.d.ts new file mode 100644 index 000000000..c92a5348c --- /dev/null +++ b/src/vitest-browser.d.ts @@ -0,0 +1,57 @@ +/// + +import type { RenderOptions, TemplateResult } from 'lit'; +import type { DiffOptions } from '@open-wc/semantic-dom-diff/get-diffable-html'; +import type { Assertion } from 'vitest'; + +interface LitTemplateResult { + processor: any; + strings: TemplateStringsArray; + type: string; + values: readonly unknown[]; +} + +type RenderResult = + | LitTemplateResult + | TemplateResult + | TemplateResult[] + | Node + | Node[] + | string + | string[] + | number + | number[] + | boolean + | boolean[] + | null + | undefined; + +type FixtureOptions = { + container?: HTMLElement; + options?: RenderOptions; +}; + +interface DomAssertion extends Omit, 'equal' | 'to' | 'be'> { + to: DomAssertion; + be: DomAssertion; + equal(value: unknown, options?: DiffOptions): void; + equal(value: unknown, message?: string, options?: DiffOptions): void; +} + +declare module 'vitest' { + interface Assertion { + accessible(options?: object): Promise>; + dom: DomAssertion; + lightDom: DomAssertion; + shadowDom: DomAssertion; + } +} + +declare module '@vitest/browser/context' { + interface BrowserPage { + fixture( + template: RenderResult, + options?: FixtureOptions + ): Promise; + } +} diff --git a/tsconfig.json b/tsconfig.json index 35d225caa..35caa3a2d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,8 +31,7 @@ "no-incompatible-type-binding": "warning" } } - ], - "types": ["mocha"] + ] }, "include": ["src/**/*.ts"], "exclude": ["src/**/*.stories.ts"], diff --git a/vite.config.ts b/vite.config.ts index f12ba1861..08e355687 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,4 +1,23 @@ import { defineConfig } from 'vite'; +import { configDefaults } from 'vitest/config'; +import { playwright } from '@vitest/browser-playwright'; + +const externalize = (sources: string[]) => ({ + name: 'externalize-deps', + resolveId(source: string) { + return sources.includes(source) ? { id: source, external: true } : null; + }, +}); + +// Utilities for certain test suites in the library +const IGNORED_TESTS = [ + '**/stepper-utils.spec.ts', + '**/date-range-picker.utils.spec.ts', + '**/helpers.spec.ts', + '**/tree-utils.spec.ts', + '**/validity-helpers.spec.ts', + '**/utils.spec.ts', +]; // https://vitejs.dev/config/ export default defineConfig({ @@ -21,4 +40,36 @@ export default defineConfig({ }, }, }, + test: { + mockReset: true, + onConsoleLog(log, type) { + return !(type === 'stderr' && log.startsWith('Lit is in dev mode')); + }, + exclude: [...configDefaults.exclude, ...IGNORED_TESTS], + include: ['src/**/*.spec.ts'], + browser: { + enabled: true, + provider: playwright(), + instances: [{ browser: 'chromium', headless: true }], + screenshotFailures: false, + }, + setupFiles: ['./vitest.setup.ts'], + coverage: { + enabled: false, + provider: 'v8', + reportOnFailure: true, + reporter: ['lcov', 'html'], + reportsDirectory: './coverage', + include: ['src/**/*.ts'], + exclude: [ + 'src/**/*.spec.ts', + 'src/**/*.d.ts', + 'src/**/themes/**', + 'src/animations/presets/**', + 'src/index.ts', + 'src/extras/**', + ], + }, + }, + plugins: [externalize(['/__web-dev-server__web-socket.js'])], }); diff --git a/vitest.setup.ts b/vitest.setup.ts new file mode 100644 index 000000000..d750beb62 --- /dev/null +++ b/vitest.setup.ts @@ -0,0 +1,8 @@ +import { chai } from 'vitest'; +import chaiDom from 'chai-dom'; +import { chaiDomDiff } from '@open-wc/semantic-dom-diff'; +import { chaiA11yAxe } from 'chai-a11y-axe'; + +chai.use(chaiDom); +chai.use(chaiDomDiff); +chai.use(chaiA11yAxe); diff --git a/web-test-runner.config.mjs b/web-test-runner.config.mjs deleted file mode 100644 index 54210962f..000000000 --- a/web-test-runner.config.mjs +++ /dev/null @@ -1,36 +0,0 @@ -import { fileURLToPath } from 'node:url'; -import { playwrightLauncher } from '@web/test-runner-playwright'; -import { esbuildPlugin } from '@web/dev-server-esbuild'; - -export default /** @type {import("@web/test-runner").TestRunnerConfig} */ ({ - files: ['src/**/*.spec.ts'], - browsers: [playwrightLauncher({ product: 'chromium', headless: true })], - - /** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */ - // esbuildTarget: 'auto', - - /** Configure bare import resolve plugin */ - nodeResolve: { - exportConditions: ['browser', 'production'], - }, - - coverageConfig: { - exclude: ['node_modules/**/*', '**/themes/**'], - }, - - testFramework: { - config: { - timeout: 3000, - }, - }, - - plugins: [ - esbuildPlugin({ - ts: true, - tsconfig: fileURLToPath(new URL('./tsconfig.json', import.meta.url)), - }), - ], - - // See documentation for all available options - // https://modern-web.dev/docs/test-runner/cli-and-configuration/#configuration-file -});